Merge of the GTK+ asynchronous file chooser branch. Please see the
authorKristian Rietveld <kris@imendio.com>
Mon, 1 May 2006 21:41:12 +0000 (21:41 +0000)
committerKristian Rietveld <kristian@src.gnome.org>
Mon, 1 May 2006 21:41:12 +0000 (21:41 +0000)
2006-05-01  Kristian Rietveld  <kris@imendio.com>

Merge of the GTK+ asynchronous file chooser branch.  Please see
the kris-asynch-branch for more detailed ChangeLog entries.

* configure.in: increase binary version to 2.9.0.

* gtk.symbols:
* gtkfilechooser.c:
* gtkfilechooserbutton.c:
* gtkfilechooserdefault.c:
* gtkfilechooserdialog.c:
* gtkfilechooserembed.c:
* gtkfilechooserembed.h:
* gtkfilechooserentry.c:
* gtkfilechooserentry.h:
* gtkfilechooserprivate.h:
* gtkfilesystem.c:
* gtkfilesystem.h:
* gtkfilesystemmodel.c:
* gtkfilesystemmodel.h:
* gtkfilesystemunix.c:
* gtkpathbar.c:
* gtkpathbar.h:
Merge from kris-async-branch.

20 files changed:
ChangeLog
ChangeLog.pre-2-10
configure.in
gtk/gtk.symbols
gtk/gtkfilechooser.c
gtk/gtkfilechooserbutton.c
gtk/gtkfilechooserdefault.c
gtk/gtkfilechooserdialog.c
gtk/gtkfilechooserembed.c
gtk/gtkfilechooserembed.h
gtk/gtkfilechooserentry.c
gtk/gtkfilechooserentry.h
gtk/gtkfilechooserprivate.h
gtk/gtkfilesystem.c
gtk/gtkfilesystem.h
gtk/gtkfilesystemmodel.c
gtk/gtkfilesystemmodel.h
gtk/gtkfilesystemunix.c
gtk/gtkpathbar.c
gtk/gtkpathbar.h

index eabb68bae18285520b1495b1ea19fb4adf18619a..d449ffc51b29cfd24a2953fa8bfdc1b504338f64 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,29 @@
+2006-05-01  Kristian Rietveld  <kris@imendio.com>
+
+       Merge of the GTK+ asynchronous file chooser branch.  Please see
+       the kris-asynch-branch for more detailed ChangeLog entries.
+
+       * configure.in: increase binary version to 2.9.0.
+
+       * gtk.symbols:
+       * gtkfilechooser.c:
+       * gtkfilechooserbutton.c:
+       * gtkfilechooserdefault.c:
+       * gtkfilechooserdialog.c:
+       * gtkfilechooserembed.c:
+       * gtkfilechooserembed.h:
+       * gtkfilechooserentry.c:
+       * gtkfilechooserentry.h:
+       * gtkfilechooserprivate.h:
+       * gtkfilesystem.c:
+       * gtkfilesystem.h:
+       * gtkfilesystemmodel.c:
+       * gtkfilesystemmodel.h:
+       * gtkfilesystemunix.c:
+       * gtkpathbar.c:
+       * gtkpathbar.h:
+       Merge from kris-async-branch.
+
 2006-05-01  Matthias Clasen  <mclasen@dhcp83-48.boston.redhat.com>
 
        * NEWS: Updates
index eabb68bae18285520b1495b1ea19fb4adf18619a..d449ffc51b29cfd24a2953fa8bfdc1b504338f64 100644 (file)
@@ -1,3 +1,29 @@
+2006-05-01  Kristian Rietveld  <kris@imendio.com>
+
+       Merge of the GTK+ asynchronous file chooser branch.  Please see
+       the kris-asynch-branch for more detailed ChangeLog entries.
+
+       * configure.in: increase binary version to 2.9.0.
+
+       * gtk.symbols:
+       * gtkfilechooser.c:
+       * gtkfilechooserbutton.c:
+       * gtkfilechooserdefault.c:
+       * gtkfilechooserdialog.c:
+       * gtkfilechooserembed.c:
+       * gtkfilechooserembed.h:
+       * gtkfilechooserentry.c:
+       * gtkfilechooserentry.h:
+       * gtkfilechooserprivate.h:
+       * gtkfilesystem.c:
+       * gtkfilesystem.h:
+       * gtkfilesystemmodel.c:
+       * gtkfilesystemmodel.h:
+       * gtkfilesystemunix.c:
+       * gtkpathbar.c:
+       * gtkpathbar.h:
+       Merge from kris-async-branch.
+
 2006-05-01  Matthias Clasen  <mclasen@dhcp83-48.boston.redhat.com>
 
        * NEWS: Updates
index 857bf9d53d5b0c7d944c4cdc08d5565ba8f5a1a6..29f2d84fe2cb8590986766b930bd17bfc4021781 100644 (file)
@@ -28,7 +28,7 @@ m4_define([gtk_api_version], [2.0])
 # for GTK+.
 #
 #GTK_BINARY_VERSION=$GTK_MAJOR_VERSION.$GTK_MINOR_VERSION.$LT_CURRENT
-m4_define([gtk_binary_version], [2.4.0])
+m4_define([gtk_binary_version], [2.9.0])
 
 # required versions of other packages
 m4_define([glib_required_version], [2.10.1])
index e4fba9775db54a3c3d7f78189e317291d7ea2467..b55a6f9a352fe2f0f64efa246ef634908af022b2 100644 (file)
@@ -1392,6 +1392,7 @@ gtk_file_info_copy
 gtk_file_info_free
 gtk_file_info_get_display_key
 gtk_file_info_get_display_name
+gtk_file_info_get_icon_name
 gtk_file_info_get_is_folder
 gtk_file_info_get_is_hidden
 gtk_file_info_get_mime_type
@@ -1399,7 +1400,9 @@ gtk_file_info_get_modification_time
 gtk_file_info_get_size
 gtk_file_info_get_type G_GNUC_CONST
 gtk_file_info_new
+gtk_file_info_render_icon
 gtk_file_info_set_display_name
+gtk_file_info_set_icon_name
 gtk_file_info_set_is_folder
 gtk_file_info_set_is_hidden
 gtk_file_info_set_mime_type
@@ -1409,9 +1412,11 @@ gtk_file_path_get_type G_GNUC_CONST
 gtk_file_paths_copy
 gtk_file_paths_free
 gtk_file_paths_sort
+gtk_file_system_cancel_operation
 gtk_file_system_create_folder
 gtk_file_system_error_quark
 gtk_file_system_filename_to_path
+gtk_file_system_get_info
 gtk_file_system_get_folder
 gtk_file_system_get_parent
 gtk_file_system_get_type G_GNUC_CONST
@@ -1425,11 +1430,11 @@ gtk_file_system_path_is_local
 gtk_file_system_path_to_filename
 gtk_file_system_path_to_uri
 gtk_file_system_remove_bookmark
-gtk_file_system_render_icon
 gtk_file_system_uri_to_path
 gtk_file_system_volume_free
 gtk_file_system_volume_get_base_path
 gtk_file_system_volume_get_display_name
+gtk_file_system_volume_get_icon_name
 gtk_file_system_volume_get_is_mounted
 gtk_file_system_volume_mount
 gtk_file_system_volume_render_icon
index 3f9b943f802c152ae0ed3a26c56e1b745634d2e5..55689952b3a0e0e741b2c0d984629f5540b3b82c 100644 (file)
@@ -717,6 +717,9 @@ gtk_file_chooser_get_current_folder (GtkFileChooser *chooser)
   file_system = _gtk_file_chooser_get_file_system (chooser);
 
   path = _gtk_file_chooser_get_current_folder_path (chooser);
+  if (!path)
+    return NULL;
+
   filename = gtk_file_system_path_to_filename (file_system, path);
   gtk_file_path_free (path);
 
index 390ded1b14511fae7dd36e1a2eabe0001388e1e3..a3b202d0cbb8344c5105b4c20d6f7bdeb7305242 100644 (file)
@@ -93,6 +93,8 @@ enum
   DISPLAY_NAME_COLUMN,
   TYPE_COLUMN,
   DATA_COLUMN,
+  IS_FOLDER_COLUMN,
+  HANDLE_COLUMN,
   NUM_COLUMNS
 };
 
@@ -142,6 +144,10 @@ struct _GtkFileChooserButtonPrivate
   gulong fs_volumes_changed_id;
   gulong fs_bookmarks_changed_id;
 
+  GtkFileSystemHandle *dnd_select_folder_handle;
+  GtkFileSystemHandle *update_button_handle;
+  GSList *change_icon_theme_handles;
+
   gint icon_size;
 
   guint8 n_special;
@@ -229,8 +235,9 @@ static void     gtk_file_chooser_button_screen_changed     (GtkWidget        *wi
 
 /* Utility Functions */
 static GtkIconTheme *get_icon_theme               (GtkWidget            *widget);
-static gchar        *get_display_name_for_path    (GtkFileSystem     *fs,
-                                                  const GtkFilePath *path);
+static void          set_info_for_path_at_iter         (GtkFileChooserButton *fs,
+                                                       const GtkFilePath    *path,
+                                                       GtkTreeIter          *iter);
 
 static gint          model_get_type_position      (GtkFileChooserButton *button,
                                                   RowType               row_type);
@@ -451,7 +458,9 @@ gtk_file_chooser_button_init (GtkFileChooserButton *button)
                                        GDK_TYPE_PIXBUF, /* Icon */
                                        G_TYPE_STRING,   /* Display Name */
                                        G_TYPE_CHAR,     /* Row Type */
-                                       G_TYPE_POINTER   /* Volume || Path */));
+                                       G_TYPE_POINTER   /* Volume || Path */,
+                                       G_TYPE_BOOLEAN   /* Is Folder? */,
+                                       G_TYPE_OBJECT    /* handle */));
 
   priv->combo_box = gtk_combo_box_new ();
   priv->combo_box_changed_id =
@@ -519,30 +528,21 @@ gtk_file_chooser_button_add_shortcut_folder (GtkFileChooser     *chooser,
       GtkFileChooserButtonPrivate *priv = button->priv;
       GtkTreeIter iter;
       gint pos;
-      GdkPixbuf *pixbuf;
-      gchar *display_name;
 
       pos = model_get_type_position (button, ROW_TYPE_SHORTCUT);
       pos += priv->n_shortcuts;
 
-      pixbuf = gtk_file_system_render_icon (priv->fs, path,
-                                           GTK_WIDGET (chooser),
-                                           priv->icon_size, NULL);
-      display_name = get_display_name_for_path (priv->fs, path);
-
       gtk_list_store_insert (GTK_LIST_STORE (priv->model), &iter, pos);
       gtk_list_store_set (GTK_LIST_STORE (priv->model), &iter,
-                         ICON_COLUMN, pixbuf,
-                         DISPLAY_NAME_COLUMN, display_name,
+                         ICON_COLUMN, NULL,
+                         DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
                          TYPE_COLUMN, ROW_TYPE_SHORTCUT,
                          DATA_COLUMN, gtk_file_path_copy (path),
+                         IS_FOLDER_COLUMN, FALSE,
                          -1);
+      set_info_for_path_at_iter (button, path, &iter);
       priv->n_shortcuts++;
 
-      if (pixbuf)
-       g_object_unref (pixbuf);
-      g_free (display_name);
-
       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
     }
 
@@ -868,26 +868,10 @@ gtk_file_chooser_button_finalize (GObject *object)
 {
   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
   GtkFileChooserButtonPrivate *priv = button->priv;
-  GtkTreeIter iter;
 
   if (priv->old_path)
     gtk_file_path_free (priv->old_path);
 
-  gtk_tree_model_get_iter_first (priv->model, &iter);
-
-  do
-    {
-      model_free_row_data (button, &iter);
-    }
-  while (gtk_tree_model_iter_next (priv->model, &iter));
-
-  g_object_unref (priv->model);
-  g_object_unref (priv->filter_model);
-
-  g_signal_handler_disconnect (priv->fs, priv->fs_volumes_changed_id);
-  g_signal_handler_disconnect (priv->fs, priv->fs_bookmarks_changed_id);
-  g_object_unref (priv->fs);
-
   if (G_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->finalize != NULL)
     (*G_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->finalize) (object);
 }
@@ -901,9 +885,65 @@ gtk_file_chooser_button_destroy (GtkObject *object)
 {
   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
   GtkFileChooserButtonPrivate *priv = button->priv;
+  GtkTreeIter iter;
+  GSList *l;
 
   if (priv->dialog != NULL)
-    gtk_widget_destroy (priv->dialog);
+    {
+      gtk_widget_destroy (priv->dialog);
+      priv->dialog = NULL;
+    }
+
+  gtk_tree_model_get_iter_first (priv->model, &iter);
+
+  do
+    {
+      model_free_row_data (button, &iter);
+    }
+  while (gtk_tree_model_iter_next (priv->model, &iter));
+
+  if (priv->dnd_select_folder_handle)
+    {
+      gtk_file_system_cancel_operation (priv->dnd_select_folder_handle);
+      priv->dnd_select_folder_handle = NULL;
+    }
+
+  if (priv->update_button_handle)
+    {
+      gtk_file_system_cancel_operation (priv->update_button_handle);
+      priv->update_button_handle = NULL;
+    }
+
+  if (priv->change_icon_theme_handles)
+    {
+      for (l = priv->change_icon_theme_handles; l; l = l->next)
+        {
+          GtkFileSystemHandle *handle = GTK_FILE_SYSTEM_HANDLE (l->data);
+          gtk_file_system_cancel_operation (handle);
+        }
+      g_slist_free (priv->change_icon_theme_handles);
+      priv->change_icon_theme_handles = NULL;
+    }
+
+  if (priv->model)
+    {
+      g_object_unref (priv->model);
+      priv->model = NULL;
+    }
+
+  if (priv->filter_model)
+    {
+      g_object_unref (priv->filter_model);
+      priv->filter_model = NULL;
+    }
+
+  if (priv->fs)
+    {
+      g_signal_handler_disconnect (priv->fs, priv->fs_volumes_changed_id);
+      g_signal_handler_disconnect (priv->fs, priv->fs_bookmarks_changed_id);
+      g_object_unref (priv->fs);
+      priv->fs = NULL;
+    }
 
   if (GTK_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->destroy != NULL)
     (*GTK_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->destroy) (object);
@@ -914,6 +954,76 @@ gtk_file_chooser_button_destroy (GtkObject *object)
  *  GtkWidget Functions  *
  * ********************* */
 
+struct DndSelectFolderData
+{
+  GtkFileChooserButton *button;
+  GtkFileChooserAction action;
+  GtkFilePath *path;
+  gchar **uris;
+  guint i;
+  gboolean selected;
+};
+
+static void
+dnd_select_folder_get_info_cb (GtkFileSystemHandle *handle,
+                              const GtkFileInfo   *info,
+                              const GError        *error,
+                              gpointer             user_data)
+{
+  gboolean cancelled = handle->cancelled;
+  struct DndSelectFolderData *data = user_data;
+
+  if (handle != data->button->priv->dnd_select_folder_handle)
+    {
+      g_object_unref (data->button);
+      gtk_file_path_free (data->path);
+      g_strfreev (data->uris);
+      g_free (data);
+
+      g_object_unref (handle);
+      return;
+    }
+
+  data->button->priv->dnd_select_folder_handle = NULL;
+
+  if (!cancelled && !error && info != NULL)
+    {
+      data->selected = 
+       (((data->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
+          gtk_file_info_get_is_folder (info)) ||
+         (data->action == GTK_FILE_CHOOSER_ACTION_OPEN &&
+          !gtk_file_info_get_is_folder (info))) &&
+        _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (data->button->priv->dialog),
+                                       data->path, NULL));
+    }
+  else
+    data->selected = FALSE;
+
+  if (data->selected || data->uris[++data->i] == NULL)
+    {
+      g_object_unref (data->button);
+      gtk_file_path_free (data->path);
+      g_strfreev (data->uris);
+      g_free (data);
+
+      g_object_unref (handle);
+      return;
+    }
+
+  if (data->path)
+    gtk_file_path_free (data->path);
+
+  data->path = gtk_file_system_uri_to_path (handle->file_system,
+                                           data->uris[data->i]);
+
+  data->button->priv->dnd_select_folder_handle =
+    gtk_file_system_get_info (handle->file_system, data->path,
+                             GTK_FILE_INFO_IS_FOLDER,
+                             dnd_select_folder_get_info_cb, user_data);
+
+  g_object_unref (handle);
+}
+
 static void
 gtk_file_chooser_button_drag_data_received (GtkWidget       *widget,
                                            GdkDragContext   *context,
@@ -943,59 +1053,30 @@ gtk_file_chooser_button_drag_data_received (GtkWidget         *widget,
     case TEXT_URI_LIST:
       {
        gchar **uris;
-       GtkFilePath *base_path;
-       guint i;
-       gboolean selected;
+       struct DndSelectFolderData *info;
 
        uris = gtk_selection_data_get_uris (data);
        
        if (uris == NULL)
          break;
 
-       selected = FALSE;
-       for (i = 0; !selected && uris[i] != NULL; i++)
-         {
-           path = gtk_file_system_uri_to_path (priv->fs, uris[i]);
-
-           base_path = NULL;
-           if (path != NULL &&
-               gtk_file_system_get_parent (priv->fs, path, &base_path, NULL))
-             {
-               GtkFileFolder *folder;
-               GtkFileInfo *info;
-
-               folder = gtk_file_system_get_folder (priv->fs, base_path,
-                                                    GTK_FILE_INFO_IS_FOLDER,
-                                                    NULL);
-
-               info = gtk_file_folder_get_info (folder, path, NULL);
-
-               if (info != NULL)
-                 {
-                   GtkFileChooserAction action;
+       info = g_new0 (struct DndSelectFolderData, 1);
+       info->button = g_object_ref (button);
+       info->i = 0;
+       info->uris = uris;
+       info->selected = FALSE;
+       g_object_get (priv->dialog, "action", &info->action, NULL);
 
-                   g_object_get (priv->dialog, "action", &action, NULL);
-
-                   selected = 
-                     (((action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
-                        gtk_file_info_get_is_folder (info)) ||
-                       (action == GTK_FILE_CHOOSER_ACTION_OPEN &&
-                        !gtk_file_info_get_is_folder (info))) &&
-                       _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (priv->dialog),
-                                                      path, NULL));
-
-                   gtk_file_info_free (info);
-                 }
-               else
-                 selected = FALSE;
-
-               gtk_file_path_free (base_path);
-             }
+       info->path = gtk_file_system_uri_to_path (priv->fs,
+                                                 info->uris[info->i]);
 
-           gtk_file_path_free (path);
-         }
+       if (priv->dnd_select_folder_handle)
+         gtk_file_system_cancel_operation (priv->dnd_select_folder_handle);
 
-       g_strfreev (uris);
+       priv->dnd_select_folder_handle =
+         gtk_file_system_get_info (priv->fs, info->path,
+                                   GTK_FILE_INFO_IS_FOLDER,
+                                   dnd_select_folder_get_info_cb, info);
       }
       break;
 
@@ -1096,6 +1177,64 @@ gtk_file_chooser_button_mnemonic_activate (GtkWidget *widget,
 }
 
 /* Changes the icons wherever it is needed */
+struct ChangeIconThemeData
+{
+  GtkFileChooserButton *button;
+  GtkTreeRowReference *row_ref;
+};
+
+static void
+change_icon_theme_get_info_cb (GtkFileSystemHandle *handle,
+                              const GtkFileInfo   *info,
+                              const GError        *error,
+                              gpointer             user_data)
+{
+  gboolean cancelled = handle->cancelled;
+  GdkPixbuf *pixbuf;
+  struct ChangeIconThemeData *data = user_data;
+
+  if (!g_slist_find (data->button->priv->change_icon_theme_handles, handle))
+    goto out;
+
+  data->button->priv->change_icon_theme_handles =
+    g_slist_remove (data->button->priv->change_icon_theme_handles, handle);
+
+  if (cancelled || error)
+    goto out;
+
+  pixbuf = gtk_file_info_render_icon (info, GTK_WIDGET (data->button),
+                                     data->button->priv->icon_size, NULL);
+
+  if (pixbuf)
+    {
+      gint width = 0;
+      GtkTreeIter iter;
+      GtkTreePath *path;
+
+      width = MAX (width, gdk_pixbuf_get_width (pixbuf));
+
+      path = gtk_tree_row_reference_get_path (data->row_ref);
+      gtk_tree_model_get_iter (data->button->priv->model, &iter, path);
+      gtk_tree_path_free (path);
+
+      gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
+                         ICON_COLUMN, pixbuf,
+                         -1);
+      g_object_unref (pixbuf);
+
+      g_object_set (data->button->priv->icon_cell,
+                   "width", width,
+                   NULL);
+    }
+
+out:
+  g_object_unref (data->button);
+  gtk_tree_row_reference_free (data->row_ref);
+  g_free (data);
+
+  g_object_unref (handle);
+}
+
 static void
 change_icon_theme (GtkFileChooserButton *button)
 {
@@ -1103,7 +1242,16 @@ change_icon_theme (GtkFileChooserButton *button)
   GtkSettings *settings;
   GtkIconTheme *theme;
   GtkTreeIter iter;
-  gint width, height;
+  GSList *l;
+  gint width = 0, height = 0;
+
+  for (l = button->priv->change_icon_theme_handles; l; l = l->next)
+    {
+      GtkFileSystemHandle *handle = GTK_FILE_SYSTEM_HANDLE (l->data);
+      gtk_file_system_cancel_operation (handle);
+    }
+  g_slist_free (button->priv->change_icon_theme_handles);
+  button->priv->change_icon_theme_handles = NULL;
 
   settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (button)));
 
@@ -1138,9 +1286,25 @@ change_icon_theme (GtkFileChooserButton *button)
        case ROW_TYPE_BOOKMARK:
        case ROW_TYPE_CURRENT_FOLDER:
          if (data)
-           pixbuf = gtk_file_system_render_icon (priv->fs, data,
-                                                 GTK_WIDGET (button),
-                                                 priv->icon_size, NULL);
+           {
+             GtkTreePath *path;
+             GtkFileSystemHandle *handle;
+             struct ChangeIconThemeData *info;
+
+             info = g_new0 (struct ChangeIconThemeData, 1);
+             info->button = g_object_ref (button);
+             path = gtk_tree_model_get_path (priv->model, &iter);
+             info->row_ref = gtk_tree_row_reference_new (priv->model, path);
+             gtk_tree_path_free (path);
+
+             handle =
+               gtk_file_system_get_info (priv->fs, data, GTK_FILE_INFO_ICON,
+                                         change_icon_theme_get_info_cb,
+                                         info);
+             button->priv->change_icon_theme_handles =
+               g_slist_append (button->priv->change_icon_theme_handles, handle);
+             pixbuf = NULL;
+           }
          else
            pixbuf = gtk_icon_theme_load_icon (theme, FALLBACK_ICON_NAME,
                                               priv->icon_size, 0, NULL);
@@ -1215,43 +1379,92 @@ get_icon_theme (GtkWidget *widget)
   return gtk_icon_theme_get_default ();
 }
 
-static gchar *
-get_display_name_for_path (GtkFileSystem     *fs,
-                          const GtkFilePath *path)
+
+struct SetDisplayNameData
 {
-  GtkFilePath *parent_path;
-  GtkFileFolder *folder;
-  gchar *retval;
+  GtkFileChooserButton *button;
+  GtkTreeRowReference *row_ref;
+};
 
-  parent_path = NULL;
-  retval = NULL;
+static void
+set_info_get_info_cb (GtkFileSystemHandle *handle,
+                     const GtkFileInfo   *info,
+                     const GError        *error,
+                     gpointer             callback_data)
+{
+  gboolean cancelled = handle->cancelled;
+  GdkPixbuf *pixbuf;
+  GtkTreePath *path;
+  GtkTreeIter iter;
+  GtkFileSystemHandle *model_handle;
+  struct SetDisplayNameData *data = callback_data;
 
-  gtk_file_system_get_parent (fs, path, &parent_path, NULL);
+  path = gtk_tree_row_reference_get_path (data->row_ref);
+  if (!path)
+    /* Handle doesn't exist anymore in the model */
+    goto out;
 
-  folder = gtk_file_system_get_folder (fs, parent_path ? parent_path : path,
-                                      GTK_FILE_INFO_DISPLAY_NAME, NULL);
+  gtk_tree_model_get_iter (data->button->priv->model, &iter, path);
+  gtk_tree_path_free (path);
 
-  if (folder)
-    {
-      GtkFileInfo *info;
+  /* Validate the handle */
+  gtk_tree_model_get (data->button->priv->model, &iter,
+                     HANDLE_COLUMN, &model_handle,
+                     -1);
+  if (handle != model_handle)
+    goto out;
+
+  gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
+                     HANDLE_COLUMN, NULL,
+                     -1);
 
-      info = gtk_file_folder_get_info (folder, path, NULL);
-      g_object_unref (folder);
+  if (cancelled || error)
+    /* There was an error, leave the fallback name in there */
+    goto out;
 
-      if (info)
-       {
-         retval = g_strdup (gtk_file_info_get_display_name (info));
-         gtk_file_info_free (info);
-       }
-    }
+  pixbuf = gtk_file_info_render_icon (info, GTK_WIDGET (data->button),
+                                     data->button->priv->icon_size, NULL);
 
-  if (parent_path)
-    gtk_file_path_free (parent_path);
+  gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
+                     ICON_COLUMN, pixbuf,
+                     DISPLAY_NAME_COLUMN, gtk_file_info_get_display_name (info),
+                     IS_FOLDER_COLUMN, gtk_file_info_get_is_folder (info),
+                     -1);
+
+  if (pixbuf)
+    g_object_unref (pixbuf);
 
-  if (!retval)
-    retval = g_strdup (_(FALLBACK_DISPLAY_NAME));
+out:
+  g_object_unref (data->button);
+  gtk_tree_row_reference_free (data->row_ref);
+  g_free (data);
 
-  return retval;
+  g_object_unref (handle);
+}
+
+static void
+set_info_for_path_at_iter (GtkFileChooserButton *button,
+                          const GtkFilePath    *path,
+                          GtkTreeIter          *iter)
+{
+  struct SetDisplayNameData *data;
+  GtkTreePath *tree_path;
+  GtkFileSystemHandle *handle;
+
+  data = g_new0 (struct SetDisplayNameData, 1);
+  data->button = g_object_ref (button);
+
+  tree_path = gtk_tree_model_get_path (button->priv->model, iter);
+  data->row_ref = gtk_tree_row_reference_new (button->priv->model, tree_path);
+  gtk_tree_path_free (tree_path);
+
+  handle = gtk_file_system_get_info (button->priv->fs, path,
+                                    GTK_FILE_INFO_DISPLAY_NAME | GTK_FILE_INFO_IS_FOLDER | GTK_FILE_INFO_ICON,
+                                    set_info_get_info_cb, data);
+
+  gtk_list_store_set (GTK_LIST_STORE (button->priv->model), iter,
+                     HANDLE_COLUMN, handle,
+                     -1);
 }
 
 /* Shortcuts Model */
@@ -1314,12 +1527,17 @@ model_free_row_data (GtkFileChooserButton *button,
 {
   gchar type;
   gpointer data;
+  GtkFileSystemHandle *handle;
 
   gtk_tree_model_get (button->priv->model, iter,
                      TYPE_COLUMN, &type,
                      DATA_COLUMN, &data,
+                     HANDLE_COLUMN, &handle,
                      -1);
 
+  if (handle)
+    gtk_file_system_cancel_operation (handle);
+
   switch (type)
     {
     case ROW_TYPE_SPECIAL:
@@ -1336,16 +1554,70 @@ model_free_row_data (GtkFileChooserButton *button,
     }
 }
 
+static void
+model_add_special_get_info_cb (GtkFileSystemHandle *handle,
+                              const GtkFileInfo   *info,
+                              const GError        *error,
+                              gpointer             user_data)
+{
+  gboolean cancelled = handle->cancelled;
+  GtkTreeIter iter;
+  GtkTreePath *path;
+  GdkPixbuf *pixbuf;
+  GtkFileSystemHandle *model_handle;
+  struct ChangeIconThemeData *data = user_data;
+
+  path = gtk_tree_row_reference_get_path (data->row_ref);
+  if (!path)
+    /* Handle doesn't exist anymore in the model */
+    goto out;
+
+  gtk_tree_model_get_iter (data->button->priv->model, &iter, path);
+  gtk_tree_path_free (path);
+
+  gtk_tree_model_get (data->button->priv->model, &iter,
+                     HANDLE_COLUMN, &model_handle,
+                     -1);
+  if (handle != model_handle)
+    goto out;
+
+  gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
+                     HANDLE_COLUMN, NULL,
+                     -1);
+
+  if (cancelled || error)
+    goto out;
+
+  pixbuf = gtk_file_info_render_icon (info, GTK_WIDGET (data->button),
+                                     data->button->priv->icon_size, NULL);
+
+  if (pixbuf)
+    {
+      gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
+                         ICON_COLUMN, pixbuf,
+                         -1);
+      g_object_unref (pixbuf);
+    }
+
+  gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
+                     DISPLAY_NAME_COLUMN, gtk_file_info_get_display_name (info),
+                     -1);
+
+out:
+  gtk_tree_row_reference_free (data->row_ref);
+  g_free (data);
+
+  g_object_unref (handle);
+}
+
 static inline void
 model_add_special (GtkFileChooserButton *button)
 {
   const gchar *homedir;
-  const gchar *display_name;
   gchar *desktopdir = NULL;
   GtkListStore *store;
   GtkTreeIter iter;
   GtkFilePath *path;
-  GdkPixbuf *pixbuf;
   gint pos;
 
   store = GTK_LIST_STORE (button->priv->model);
@@ -1355,23 +1627,34 @@ model_add_special (GtkFileChooserButton *button)
 
   if (homedir)
     {
+      GtkTreePath *tree_path;
+      GtkFileSystemHandle *handle;
+      struct ChangeIconThemeData *info;
+
       path = gtk_file_system_filename_to_path (button->priv->fs, homedir);
-      display_name = get_display_name_for_path (button->priv->fs, path);
-      pixbuf = gtk_file_system_render_icon (button->priv->fs, path,
-                                           GTK_WIDGET (button),
-                                           button->priv->icon_size, NULL);
       gtk_list_store_insert (store, &iter, pos);
       pos++;
+
+      info = g_new0 (struct ChangeIconThemeData, 1);
+      info->button = g_object_ref (button);
+      tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
+      info->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (store),
+                                                 tree_path);
+      gtk_tree_path_free (tree_path);
+
+      handle = gtk_file_system_get_info (button->priv->fs, path,
+                                        GTK_FILE_INFO_ICON,
+                                        model_add_special_get_info_cb, info);
+
       gtk_list_store_set (store, &iter,
-                         ICON_COLUMN, pixbuf,
-                         DISPLAY_NAME_COLUMN, display_name,
+                         ICON_COLUMN, NULL,
+                         DISPLAY_NAME_COLUMN, NULL,
                          TYPE_COLUMN, ROW_TYPE_SPECIAL,
                          DATA_COLUMN, path,
+                         IS_FOLDER_COLUMN, TRUE,
+                         HANDLE_COLUMN, handle,
                          -1);
 
-      if (pixbuf)
-       g_object_unref (pixbuf);
-      g_free (display_name);
       button->priv->n_special++;
 
 #ifndef G_OS_WIN32
@@ -1385,22 +1668,34 @@ model_add_special (GtkFileChooserButton *button)
 
   if (desktopdir)
     {
+      GtkTreePath *tree_path;
+      GtkFileSystemHandle *handle;
+      struct ChangeIconThemeData *info;
+
       path = gtk_file_system_filename_to_path (button->priv->fs, desktopdir);
       g_free (desktopdir);
-      pixbuf = gtk_file_system_render_icon (button->priv->fs, path,
-                                           GTK_WIDGET (button),
-                                           button->priv->icon_size, NULL);
       gtk_list_store_insert (store, &iter, pos);
       pos++;
+
+      info = g_new0 (struct ChangeIconThemeData, 1);
+      info->button = g_object_ref (button);
+      tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
+      info->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (store),
+                                                 tree_path);
+      gtk_tree_path_free (tree_path);
+
+      handle = gtk_file_system_get_info (button->priv->fs, path,
+                                        GTK_FILE_INFO_ICON,
+                                        model_add_special_get_info_cb, info);
+
       gtk_list_store_set (store, &iter,
                          TYPE_COLUMN, ROW_TYPE_SPECIAL,
-                         ICON_COLUMN, pixbuf,
+                         ICON_COLUMN, NULL,
                          DISPLAY_NAME_COLUMN, _(DESKTOP_DISPLAY_NAME),
                          DATA_COLUMN, path,
+                         IS_FOLDER_COLUMN, TRUE,
                          -1);
 
-      if (pixbuf)
-       g_object_unref (pixbuf);
       button->priv->n_special++;
     }
 }
@@ -1438,6 +1733,7 @@ model_add_volumes (GtkFileChooserButton *button,
                          DISPLAY_NAME_COLUMN, display_name,
                          TYPE_COLUMN, ROW_TYPE_VOLUME,
                          DATA_COLUMN, volumes->data,
+                         IS_FOLDER_COLUMN, TRUE,
                          -1);
 
       if (pixbuf)
@@ -1473,32 +1769,24 @@ model_add_bookmarks (GtkFileChooserButton *button,
                          DISPLAY_NAME_COLUMN, NULL,
                          TYPE_COLUMN, ROW_TYPE_BOOKMARK_SEPARATOR,
                          DATA_COLUMN, NULL,
+                         IS_FOLDER_COLUMN, FALSE,
                          -1);
       button->priv->has_bookmark_separator = TRUE;
     }
 
   do
     {
-      GdkPixbuf *pixbuf;
-      gchar *display_name;
-
       pos++;
-      pixbuf = gtk_file_system_render_icon (button->priv->fs, bookmarks->data,
-                                           GTK_WIDGET (button),
-                                           button->priv->icon_size, NULL);
-      display_name = get_display_name_for_path (button->priv->fs,
-                                               bookmarks->data);
 
       gtk_list_store_insert (store, &iter, pos);
       gtk_list_store_set (store, &iter,
-                         ICON_COLUMN, pixbuf,
-                         DISPLAY_NAME_COLUMN, display_name,
+                         ICON_COLUMN, NULL,
+                         DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
                          TYPE_COLUMN, ROW_TYPE_BOOKMARK,
                          DATA_COLUMN, gtk_file_path_copy (bookmarks->data),
+                         IS_FOLDER_COLUMN, FALSE,
                          -1);
-      if (pixbuf)
-       g_object_unref (pixbuf);
-      g_free (display_name);
+      set_info_for_path_at_iter (button, bookmarks->data, &iter);
 
       button->priv->n_bookmarks++;
       bookmarks = bookmarks->next;
@@ -1513,8 +1801,6 @@ model_update_current_folder (GtkFileChooserButton *button,
   GtkListStore *store;
   GtkTreeIter iter;
   gint pos;
-  GdkPixbuf *pixbuf;
-  gchar *display_name;
 
   if (!path) 
     return;
@@ -1530,6 +1816,7 @@ model_update_current_folder (GtkFileChooserButton *button,
                          DISPLAY_NAME_COLUMN, NULL,
                          TYPE_COLUMN, ROW_TYPE_CURRENT_FOLDER_SEPARATOR,
                          DATA_COLUMN, NULL,
+                         IS_FOLDER_COLUMN, FALSE,
                          -1);
       button->priv->has_current_folder_separator = TRUE;
     }
@@ -1546,19 +1833,14 @@ model_update_current_folder (GtkFileChooserButton *button,
       model_free_row_data (button, &iter);
     }
 
-  pixbuf = gtk_file_system_render_icon (button->priv->fs, path,
-                                       GTK_WIDGET (button),
-                                       button->priv->icon_size, NULL);
-  display_name = get_display_name_for_path (button->priv->fs, path);
   gtk_list_store_set (store, &iter,
-                     ICON_COLUMN, pixbuf,
-                     DISPLAY_NAME_COLUMN, display_name,
+                     ICON_COLUMN, NULL,
+                     DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
                      TYPE_COLUMN, ROW_TYPE_CURRENT_FOLDER,
                      DATA_COLUMN, gtk_file_path_copy (path),
+                     IS_FOLDER_COLUMN, FALSE,
                      -1);
-  if (pixbuf)
-    g_object_unref (pixbuf);
-  g_free (display_name);
+  set_info_for_path_at_iter (button, path, &iter);
 }
 
 static inline void
@@ -1577,6 +1859,7 @@ model_add_other (GtkFileChooserButton *button)
                      DISPLAY_NAME_COLUMN, NULL,
                      TYPE_COLUMN, ROW_TYPE_OTHER_SEPARATOR,
                      DATA_COLUMN, NULL,
+                     IS_FOLDER_COLUMN, FALSE,
                      -1);
   button->priv->has_other_separator = TRUE;
   pos++;
@@ -1587,6 +1870,7 @@ model_add_other (GtkFileChooserButton *button)
                      DISPLAY_NAME_COLUMN, _("Other..."),
                      TYPE_COLUMN, ROW_TYPE_OTHER,
                      DATA_COLUMN, NULL,
+                     IS_FOLDER_COLUMN, FALSE,
                      -1);
 }
 
@@ -1620,42 +1904,17 @@ model_remove_rows (GtkFileChooserButton *button,
 static inline gboolean
 test_if_path_is_visible (GtkFileSystem     *fs,
                         const GtkFilePath *path,
-                        gboolean           local_only)
+                        gboolean           local_only,
+                        gboolean           is_folder)
 {
-  GtkFilePath *parent_path;
-  GtkFileFolder *folder;
-  GtkFileInfo *info;
-
   if (!path)
     return FALSE;
 
   if (local_only && !gtk_file_system_path_is_local (fs, path))
     return FALSE;
 
-  parent_path = NULL;
-  gtk_file_system_get_parent (fs, path, &parent_path, NULL);
-
-  folder = gtk_file_system_get_folder (fs, parent_path ? parent_path : path,
-                                      GTK_FILE_INFO_IS_FOLDER, NULL);
-  gtk_file_path_free (parent_path);
-
-  if (folder)
-    {
-      info = gtk_file_folder_get_info (folder, path, NULL);
-      g_object_unref (folder);
-    }
-  else
-    info = NULL;
-
-  if (!info)
+  if (!is_folder)
     return FALSE;
-  else if (!gtk_file_info_get_is_folder (info))
-    {
-      gtk_file_info_free (info);
-      return FALSE;
-    }
-
-  gtk_file_info_free (info);
 
   return TRUE;
 }
@@ -1669,7 +1928,7 @@ filter_model_visible_func (GtkTreeModel *model,
   GtkFileChooserButtonPrivate *priv = button->priv;
   gchar type;
   gpointer data;
-  gboolean local_only, retval;
+  gboolean local_only, retval, is_folder;
 
   type = ROW_TYPE_INVALID;
   data = NULL;
@@ -1678,6 +1937,7 @@ filter_model_visible_func (GtkTreeModel *model,
   gtk_tree_model_get (model, iter,
                      TYPE_COLUMN, &type,
                      DATA_COLUMN, &data,
+                     IS_FOLDER_COLUMN, &is_folder,
                      -1);
 
   switch (type)
@@ -1688,7 +1948,7 @@ filter_model_visible_func (GtkTreeModel *model,
     case ROW_TYPE_SPECIAL:
     case ROW_TYPE_SHORTCUT:
     case ROW_TYPE_BOOKMARK:
-      retval = test_if_path_is_visible (priv->fs, data, local_only);
+      retval = test_if_path_is_visible (priv->fs, data, local_only, is_folder);
       break;
     case ROW_TYPE_VOLUME:
       {
@@ -1836,6 +2096,43 @@ update_combo_box (GtkFileChooserButton *button)
 }
 
 /* Button */
+static void
+update_label_get_info_cb (GtkFileSystemHandle *handle,
+                         const GtkFileInfo   *info,
+                         const GError        *error,
+                         gpointer             data)
+{
+  gboolean cancelled = handle->cancelled;
+  GdkPixbuf *pixbuf;
+  GtkFileChooserButton *button = data;
+  GtkFileChooserButtonPrivate *priv = button->priv;
+
+  if (handle != priv->update_button_handle)
+    goto out;
+
+  priv->update_button_handle = NULL;
+
+  if (cancelled || error)
+    goto out;
+
+  gtk_label_set_text (GTK_LABEL (priv->label), gtk_file_info_get_display_name (info));
+
+  pixbuf = gtk_file_info_render_icon (info, GTK_WIDGET (priv->image),
+                                     priv->icon_size, NULL);
+  if (!pixbuf)
+    pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (priv->image)),
+                                      FALLBACK_ICON_NAME,
+                                      priv->icon_size, 0, NULL);
+
+  gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
+  if (pixbuf)
+    g_object_unref (pixbuf);
+
+out:
+  g_object_unref (button);
+  g_object_unref (handle);
+}
+
 static void
 update_label_and_image (GtkFileChooserButton *button)
 {
@@ -1848,11 +2145,10 @@ update_label_and_image (GtkFileChooserButton *button)
   label_text = NULL;
   pixbuf = NULL;
 
-  if (paths)
+  if (paths && paths->data)
     {
-      GtkFilePath *path, *parent_path;
-      GtkFileSystemVolume *volume;
-      GtkFileFolder *folder;
+      GtkFilePath *path;
+      GtkFileSystemVolume *volume = NULL;
     
       path = paths->data;
 
@@ -1881,32 +2177,14 @@ update_label_and_image (GtkFileChooserButton *button)
            goto out;
        }
 
-      if (!pixbuf)
-       pixbuf = gtk_file_system_render_icon (priv->fs, path,
-                                             GTK_WIDGET (button),
-                                             priv->icon_size, NULL);
+      if (priv->update_button_handle)
+       gtk_file_system_cancel_operation (priv->update_button_handle);
 
-      parent_path = NULL;
-      gtk_file_system_get_parent (priv->fs, path, &parent_path, NULL);
-
-      folder = gtk_file_system_get_folder (priv->fs,
-                                          parent_path ? parent_path : path,
-                                          GTK_FILE_INFO_DISPLAY_NAME, NULL);
-      gtk_file_path_free (parent_path);
-
-      if (folder)
-       {
-         GtkFileInfo *info;
-
-         info = gtk_file_folder_get_info (folder, path, NULL);
-         g_object_unref (folder);
-
-         if (info)
-           {
-             label_text = g_strdup (gtk_file_info_get_display_name (info));
-             gtk_file_info_free (info);
-           }
-       }
+      priv->update_button_handle =
+        gtk_file_system_get_info (priv->fs, path,
+                                 GTK_FILE_INFO_DISPLAY_NAME | GTK_FILE_INFO_ICON,
+                                 update_label_get_info_cb,
+                                 g_object_ref (button));
 
      out:
       gtk_file_paths_free (paths);
@@ -1919,15 +2197,6 @@ update_label_and_image (GtkFileChooserButton *button)
     }
   else
     gtk_label_set_text (GTK_LABEL (priv->label), _(FALLBACK_DISPLAY_NAME));
-  
-  if (!pixbuf)
-    pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (button)),
-                                      FALLBACK_ICON_NAME,
-                                      priv->icon_size, 0, NULL);
-
-  gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
-  if (pixbuf)
-    g_object_unref (pixbuf);
 }
 
 
index b78370a64752a54ea559406dc93a669593c5a53a..27a7df701b9ed00fd98c33a60aeb9b7548ca4140 100644 (file)
@@ -170,6 +170,7 @@ enum {
   SHORTCUTS_COL_IS_VOLUME,
   SHORTCUTS_COL_REMOVABLE,
   SHORTCUTS_COL_PIXBUF_VISIBLE,
+  SHORTCUTS_COL_HANDLE,
   SHORTCUTS_COL_NUM_COLUMNS
 };
 
@@ -379,6 +380,8 @@ static void add_bookmark_button_clicked_cb    (GtkButton             *button,
                                               GtkFileChooserDefault *impl);
 static void remove_bookmark_button_clicked_cb (GtkButton             *button,
                                               GtkFileChooserDefault *impl);
+static void save_folder_combo_changed_cb      (GtkComboBox           *combo,
+                                              GtkFileChooserDefault *impl);
 
 static void list_icon_data_func (GtkTreeViewColumn *tree_column,
                                 GtkCellRenderer   *cell,
@@ -699,11 +702,17 @@ shortcuts_free_row_data (GtkFileChooserDefault *impl,
 {
   gpointer col_data;
   gboolean is_volume;
+  GtkFileSystemHandle *handle;
 
   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
                      SHORTCUTS_COL_DATA, &col_data,
                      SHORTCUTS_COL_IS_VOLUME, &is_volume,
+                     SHORTCUTS_COL_HANDLE, &handle,
                      -1);
+
+  if (handle)
+    gtk_file_system_cancel_operation (handle);
+
   if (!col_data)
     return;
 
@@ -809,10 +818,6 @@ gtk_file_chooser_default_finalize (GObject *object)
 
   shortcuts_free (impl);
 
-  g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
-  impl->volumes_changed_id = 0;
-  g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
-  impl->bookmarks_changed_id = 0;
   g_object_unref (impl->file_system);
 
   for (l = impl->filters; l; l = l->next)
@@ -836,8 +841,6 @@ gtk_file_chooser_default_finalize (GObject *object)
   if (impl->preview_path)
     gtk_file_path_free (impl->preview_path);
 
-  pending_select_paths_free (impl);
-
   load_remove_timer (impl);
 
   /* Free all the Models we have */
@@ -1099,9 +1102,57 @@ set_preview_widget (GtkFileChooserDefault *impl,
 }
 
 /* Re-reads all the icons for the shortcuts, used when the theme changes */
+struct ReloadIconsData
+{
+  GtkFileChooserDefault *impl;
+  GtkTreeRowReference *row_ref;
+};
+
+static void
+shortcuts_reload_icons_get_info_cb (GtkFileSystemHandle *handle,
+                                   const GtkFileInfo   *info,
+                                   const GError        *error,
+                                   gpointer             user_data)
+{
+  GdkPixbuf *pixbuf;
+  GtkTreeIter iter;
+  GtkTreePath *path;
+  gboolean cancelled = handle->cancelled;
+  struct ReloadIconsData *data = user_data;
+
+  if (!g_slist_find (data->impl->reload_icon_handles, handle))
+    goto out;
+
+  data->impl->reload_icon_handles = g_slist_remove (data->impl->reload_icon_handles, handle);
+
+  if (cancelled || error)
+    goto out;
+
+  pixbuf = gtk_file_info_render_icon (info, GTK_WIDGET (data->impl),
+                                     data->impl->icon_size, NULL);
+
+  path = gtk_tree_row_reference_get_path (data->row_ref);
+  gtk_tree_model_get_iter (GTK_TREE_MODEL (data->impl->shortcuts_model), &iter, path);
+  gtk_list_store_set (data->impl->shortcuts_model, &iter,
+                     SHORTCUTS_COL_PIXBUF, pixbuf,
+                     -1);
+  gtk_tree_path_free (path);
+
+  if (pixbuf)
+    g_object_unref (pixbuf);
+
+out:
+  gtk_tree_row_reference_free (data->row_ref);
+  g_object_unref (data->impl);
+  g_free (data);
+
+  g_object_unref (handle);
+}
+
 static void
 shortcuts_reload_icons (GtkFileChooserDefault *impl)
 {
+  GSList *l;
   GtkTreeIter iter;
 
   profile_start ("start", NULL);
@@ -1109,44 +1160,68 @@ shortcuts_reload_icons (GtkFileChooserDefault *impl)
   if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
     goto out;
 
-  do {
-    gpointer data;
-    gboolean is_volume;
-    gboolean pixbuf_visible;
-    GdkPixbuf *pixbuf;
+  for (l = impl->reload_icon_handles; l; l = l->next)
+    {
+      GtkFileSystemHandle *handle = GTK_FILE_SYSTEM_HANDLE (l->data);
+      gtk_file_system_cancel_operation (handle);
+    }
+  g_slist_free (impl->reload_icon_handles);
+  impl->reload_icon_handles = NULL;
 
-    gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
-                       SHORTCUTS_COL_DATA, &data,
-                       SHORTCUTS_COL_IS_VOLUME, &is_volume,
-                       SHORTCUTS_COL_PIXBUF_VISIBLE, &pixbuf_visible,
-                       -1);
+  do
+    {
+      gpointer data;
+      gboolean is_volume;
+      gboolean pixbuf_visible;
+      GdkPixbuf *pixbuf;
 
-    if (pixbuf_visible && data)
-      {
-       if (is_volume)
-         {
-           GtkFileSystemVolume *volume;
+      gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
+                         SHORTCUTS_COL_DATA, &data,
+                         SHORTCUTS_COL_IS_VOLUME, &is_volume,
+                         SHORTCUTS_COL_PIXBUF_VISIBLE, &pixbuf_visible,
+                         -1);
 
-           volume = data;
-           pixbuf = gtk_file_system_volume_render_icon (impl->file_system, volume, GTK_WIDGET (impl),
-                                                        impl->icon_size, NULL);
-         }
-       else
-         {
-           const GtkFilePath *path;
+      if (pixbuf_visible && data)
+        {
+         if (is_volume)
+           {
+             GtkFileSystemVolume *volume;
 
-           path = data;
-           pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
-                                                 impl->icon_size, NULL);
-         }
+             volume = data;
+             pixbuf = gtk_file_system_volume_render_icon (impl->file_system, volume, GTK_WIDGET (impl),
+                                                          impl->icon_size, NULL);
 
-       gtk_list_store_set (impl->shortcuts_model, &iter,
-                           SHORTCUTS_COL_PIXBUF, pixbuf,
-                           -1);
-       if (pixbuf)
-         g_object_unref (pixbuf);
-      }
-  } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model),&iter));
+             gtk_list_store_set (impl->shortcuts_model, &iter,
+                                 SHORTCUTS_COL_PIXBUF, pixbuf,
+                                 -1);
+
+             if (pixbuf)
+               g_object_unref (pixbuf);
+           }
+         else
+           {
+             const GtkFilePath *path;
+             struct ReloadIconsData *info;
+             GtkTreePath *tree_path;
+             GtkFileSystemHandle *handle;
+
+             path = data;
+
+             info = g_new0 (struct ReloadIconsData, 1);
+             info->impl = g_object_ref (impl);
+             tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
+             info->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->shortcuts_model), tree_path);
+             gtk_tree_path_free (tree_path);
+
+             handle = gtk_file_system_get_info (impl->file_system, path,
+                                                GTK_FILE_INFO_ICON,
+                                                shortcuts_reload_icons_get_info_cb,
+                                                info);
+             impl->reload_icon_handles = g_slist_append (impl->reload_icon_handles, handle);
+           }
+       }
+    }
+  while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model),&iter));
 
  out:
 
@@ -1183,83 +1258,203 @@ shortcuts_find_current_folder (GtkFileChooserDefault *impl)
   shortcuts_find_folder (impl, impl->current_folder);
 }
 
-/* Convenience function to get the display name and icon info for a path */
-static GtkFileInfo *
-get_file_info (GtkFileSystem      *file_system, 
-              const GtkFilePath  *path, 
-              gboolean            name_only, 
-              GError            **error)
+/* Removes the specified number of rows from the shortcuts list */
+static void
+shortcuts_remove_rows (GtkFileChooserDefault *impl,
+                      int                    start_row,
+                      int                    n_rows)
+{
+  GtkTreePath *path;
+
+  path = gtk_tree_path_new_from_indices (start_row, -1);
+
+  for (; n_rows; n_rows--)
+    {
+      GtkTreeIter iter;
+
+      if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
+       g_assert_not_reached ();
+
+      shortcuts_free_row_data (impl, &iter);
+      gtk_list_store_remove (impl->shortcuts_model, &iter);
+    }
+
+  gtk_tree_path_free (path);
+}
+
+static void
+shortcuts_update_count (GtkFileChooserDefault *impl,
+                       ShortcutsIndex         type,
+                       gint                   value)
 {
-  GtkFilePath *parent_path;
-  GtkFileFolder *parent_folder;
-  GtkFileInfo *info;
-  GError *tmp = NULL;
+  switch (type)
+    {
+      case SHORTCUTS_HOME:
+       if (value < 0)
+         impl->has_home = FALSE;
+       else
+         impl->has_home = TRUE;
+       break;
 
-  profile_start ("start", (char *) path);
+      case SHORTCUTS_DESKTOP:
+       if (value < 0)
+         impl->has_desktop = FALSE;
+       else
+         impl->has_desktop = TRUE;
+       break;
+
+      case SHORTCUTS_VOLUMES:
+       impl->num_volumes += value;
+       break;
+
+      case SHORTCUTS_SHORTCUTS:
+       impl->num_shortcuts += value;
+       break;
+
+      case SHORTCUTS_BOOKMARKS:
+       impl->num_bookmarks += value;
+       break;
+
+      case SHORTCUTS_CURRENT_FOLDER:
+       if (value < 0)
+         impl->shortcuts_current_folder_active = FALSE;
+       else
+         impl->shortcuts_current_folder_active = TRUE;
+       break;
+
+      default:
+       /* nothing */
+       break;
+    }
+}
+
+struct ShortcutsInsertRequest
+{
+  GtkFileChooserDefault *impl;
+  GtkFilePath *parent_path;
+  GtkFilePath *path;
+  int pos;
+  char *label_copy;
+  GtkTreeRowReference *row_ref;
+  ShortcutsIndex type;
+  gboolean name_only;
+  gboolean removable;
+};
 
-  parent_path = NULL;
-  info = NULL;
+static void
+get_file_info_finished (GtkFileSystemHandle *handle,
+                       const GtkFileInfo   *info,
+                       const GError        *error,
+                       gpointer             data)
+{
+  gint pos = -1;
+  gboolean cancelled = handle->cancelled;
+  gboolean is_volume = FALSE;
+  GdkPixbuf *pixbuf;
+  GtkTreePath *path;
+  GtkTreeIter iter;
+  GtkFileSystemHandle *model_handle;
+  struct ShortcutsInsertRequest *request = data;
 
-  if (!gtk_file_system_get_parent (file_system, path, &parent_path, &tmp))
+  path = gtk_tree_row_reference_get_path (request->row_ref);
+  if (!path)
+    /* Handle doesn't exist anymore in the model */
     goto out;
 
-  parent_folder = gtk_file_system_get_folder (file_system, parent_path ? parent_path : path,
-                                             GTK_FILE_INFO_DISPLAY_NAME
-                                             | (name_only ? 0 : GTK_FILE_INFO_IS_FOLDER),
-                                             &tmp);
-  if (!parent_folder)
+  pos = gtk_tree_path_get_indices (path)[0];
+  gtk_tree_model_get_iter (GTK_TREE_MODEL (request->impl->shortcuts_model),
+                          &iter, path);
+  gtk_tree_path_free (path);
+
+  /* validate handle, else goto out */
+  gtk_tree_model_get (GTK_TREE_MODEL (request->impl->shortcuts_model), &iter,
+                     SHORTCUTS_COL_HANDLE, &model_handle,
+                     -1);
+  if (handle != model_handle)
     goto out;
 
-  info = gtk_file_folder_get_info (parent_folder, parent_path ? path : NULL, &tmp);
-  g_object_unref (parent_folder);
+  /* set the handle to NULL in the model (we unref later on) */
+  gtk_list_store_set (request->impl->shortcuts_model, &iter,
+                     SHORTCUTS_COL_HANDLE, NULL,
+                     -1);
 
- out:
-  if (parent_path)
-    gtk_file_path_free (parent_path);
+  if (cancelled)
+    goto out;
 
-  if (tmp)
+  if (!info)
     {
-      g_set_error (error,
-                  GTK_FILE_CHOOSER_ERROR,
-                  GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
-                  _("Could not get information about '%s': %s"), 
-                  gtk_file_path_get_string (path),
-                  tmp->message);
-      g_error_free (tmp);
-    }
+      gtk_list_store_remove (request->impl->shortcuts_model, &iter);
+      shortcuts_update_count (request->impl, request->type, -1);
 
-  profile_end ("end", (char *) path);
+      if (request->type == SHORTCUTS_HOME)
+        {
+         const char *home = g_get_home_dir ();
+         GtkFilePath *home_path;
 
-  return info;
-}
+         home_path = gtk_file_system_filename_to_path (request->impl->file_system, home);
+         error_getting_info_dialog (request->impl, home_path, g_error_copy (error));
+         gtk_file_path_free (home_path);
+       }
+      else if (request->type == SHORTCUTS_CURRENT_FOLDER)
+        {
+         /* Remove the current folder separator */
+         gint separator_pos = shortcuts_get_index (request->impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
+         shortcuts_remove_rows (request->impl, separator_pos, 1);
+        }
 
-/* Returns whether a path is a folder */
-static gboolean
-check_is_folder (GtkFileSystem      *file_system, 
-                const GtkFilePath  *path, 
-                GError            **error)
-{
-  GtkFileFolder *folder;
+      goto out;
+    }
+  
+  if (!request->label_copy)
+    request->label_copy = g_strdup (gtk_file_info_get_display_name (info));
+  pixbuf = gtk_file_info_render_icon (info, GTK_WIDGET (request->impl),
+                                     request->impl->icon_size, NULL);
 
-  profile_start ("start", (char *) path);
+  gtk_list_store_set (request->impl->shortcuts_model, &iter,
+                     SHORTCUTS_COL_PIXBUF, pixbuf,
+                     SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
+                     SHORTCUTS_COL_NAME, request->label_copy,
+                     SHORTCUTS_COL_IS_VOLUME, is_volume,
+                     SHORTCUTS_COL_REMOVABLE, request->removable,
+                     -1);
 
-  folder = gtk_file_system_get_folder (file_system, path, 0, error);
-  if (!folder)
+  if (request->impl->shortcuts_filter_model)
+    gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (request->impl->shortcuts_filter_model));
+
+  if (request->type == SHORTCUTS_CURRENT_FOLDER
+      && request->impl->save_folder_combo != NULL)
     {
-      profile_end ("end - is not folder", (char *) path);
-      return FALSE;
+      /* The current folder is updated via _activate_iter(), don't
+       * have save_folder_combo_changed_cb() call _activate_iter()
+       * again.
+       */
+      g_signal_handlers_block_by_func (request->impl->save_folder_combo,
+                                      G_CALLBACK (save_folder_combo_changed_cb),
+                                      request->impl);
+      gtk_combo_box_set_active (GTK_COMBO_BOX (request->impl->save_folder_combo), pos);
+      g_signal_handlers_unblock_by_func (request->impl->save_folder_combo,
+                                        G_CALLBACK (save_folder_combo_changed_cb),
+                                        request->impl);
     }
 
-  g_object_unref (folder);
+  if (pixbuf)
+    g_object_unref (pixbuf);
 
-  profile_end ("end", (char *) path);
-  return TRUE;
+out:
+  g_object_unref (request->impl);
+  gtk_file_path_free (request->parent_path);
+  gtk_file_path_free (request->path);
+  gtk_tree_row_reference_free (request->row_ref);
+  g_free (request->label_copy);
+  g_free (request);
+
+  g_object_unref (handle);
 }
 
 /* Inserts a path in the shortcuts tree, making a copy of it; alternatively,
  * inserts a volume.  A position of -1 indicates the end of the tree.
  */
-static gboolean
+static void
 shortcuts_insert_path (GtkFileChooserDefault *impl,
                       int                    pos,
                       gboolean               is_volume,
@@ -1267,11 +1462,11 @@ shortcuts_insert_path (GtkFileChooserDefault *impl,
                       const GtkFilePath     *path,
                       const char            *label,
                       gboolean               removable,
-                      GError               **error)
+                      ShortcutsIndex         type)
 {
   char *label_copy;
-  GdkPixbuf *pixbuf;
-  gpointer data;
+  GdkPixbuf *pixbuf = NULL;
+  gpointer data = NULL;
   GtkTreeIter iter;
 
   profile_start ("start", is_volume ? "volume" : (char *) path);
@@ -1285,32 +1480,54 @@ shortcuts_insert_path (GtkFileChooserDefault *impl,
     }
   else
     {
+      struct ShortcutsInsertRequest *request;
+      GtkFileSystemHandle *handle;
+      GtkTreePath *p;
+
+      request = g_new0 (struct ShortcutsInsertRequest, 1);
+      request->impl = g_object_ref (impl);
+      request->path = gtk_file_path_copy (path);
+      request->name_only = TRUE;
+      request->removable = removable;
+      request->pos = pos;
+      request->type = type;
       if (label)
-       label_copy = g_strdup (label);
+       request->label_copy = g_strdup (label);
+
+      if (pos == -1)
+       gtk_list_store_append (impl->shortcuts_model, &iter);
       else
-       {
-         GtkFileInfo *info = get_file_info (impl->file_system, path, TRUE, error);
+       gtk_list_store_insert (impl->shortcuts_model, &iter, pos);
 
-         if (!info)
-           {
-             profile_end ("end - could not get info", (char *) path);
-             return FALSE;
-           }
+      p = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
+      request->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->shortcuts_model), p);
+      gtk_tree_path_free (p);
 
-         label_copy = g_strdup (gtk_file_info_get_display_name (info));
-         gtk_file_info_free (info);
-       }
+      handle = gtk_file_system_get_info (request->impl->file_system, request->path,
+                                        GTK_FILE_INFO_DISPLAY_NAME | GTK_FILE_INFO_IS_HIDDEN | GTK_FILE_INFO_ICON,
+                                        get_file_info_finished, request);
 
-      data = gtk_file_path_copy (path);
-      pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
-                                           impl->icon_size, NULL);
+      gtk_list_store_set (impl->shortcuts_model, &iter,
+                         SHORTCUTS_COL_DATA, gtk_file_path_copy (path),
+                         SHORTCUTS_COL_IS_VOLUME, is_volume,
+                         SHORTCUTS_COL_HANDLE, handle,
+                         -1);
+
+      shortcuts_update_count (impl, type, 1);
+
+      return;
     }
 
+  if (!data)
+    data = gtk_file_path_copy (path);
+
   if (pos == -1)
     gtk_list_store_append (impl->shortcuts_model, &iter);
   else
     gtk_list_store_insert (impl->shortcuts_model, &iter, pos);
 
+  shortcuts_update_count (impl, type, 1);
+
   gtk_list_store_set (impl->shortcuts_model, &iter,
                      SHORTCUTS_COL_PIXBUF, pixbuf,
                      SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
@@ -1318,16 +1535,34 @@ shortcuts_insert_path (GtkFileChooserDefault *impl,
                      SHORTCUTS_COL_DATA, data,
                      SHORTCUTS_COL_IS_VOLUME, is_volume,
                      SHORTCUTS_COL_REMOVABLE, removable,
+                     SHORTCUTS_COL_HANDLE, NULL,
                      -1);
 
+  if (impl->shortcuts_filter_model)
+    gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
+
+  if (type == SHORTCUTS_CURRENT_FOLDER && impl->save_folder_combo != NULL)
+    {
+      /* The current folder is updated via _activate_iter(), don't
+       * have save_folder_combo_changed_cb() call _activate_iter()
+       * again.
+       */
+      gint combo_pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER);
+      g_signal_handlers_block_by_func (impl->save_folder_combo,
+                                      G_CALLBACK (save_folder_combo_changed_cb),
+                                      impl);
+      gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), combo_pos);
+      g_signal_handlers_unblock_by_func (impl->save_folder_combo,
+                                        G_CALLBACK (save_folder_combo_changed_cb),
+                                        impl);
+    }
+
   g_free (label_copy);
 
   if (pixbuf)
     g_object_unref (pixbuf);
 
   profile_end ("end", NULL);
-
-  return TRUE;
 }
 
 /* Appends an item for the user's home directory to the shortcuts model */
@@ -1336,7 +1571,6 @@ shortcuts_append_home (GtkFileChooserDefault *impl)
 {
   const char *home;
   GtkFilePath *home_path;
-  GError *error;
 
   profile_start ("start", NULL);
 
@@ -1349,10 +1583,7 @@ shortcuts_append_home (GtkFileChooserDefault *impl)
 
   home_path = gtk_file_system_filename_to_path (impl->file_system, home);
 
-  error = NULL;
-  impl->has_home = shortcuts_insert_path (impl, -1, FALSE, NULL, home_path, NULL, FALSE, &error);
-  if (!impl->has_home)
-    error_getting_info_dialog (impl, home_path, error);
+  shortcuts_insert_path (impl, -1, FALSE, NULL, home_path, NULL, FALSE, SHORTCUTS_HOME);
 
   gtk_file_path_free (home_path);
 
@@ -1385,7 +1616,7 @@ shortcuts_append_desktop (GtkFileChooserDefault *impl)
   path = gtk_file_system_filename_to_path (impl->file_system, name);
   g_free (name);
 
-  impl->has_desktop = shortcuts_insert_path (impl, -1, FALSE, NULL, path, _("Desktop"), FALSE, NULL);
+  shortcuts_insert_path (impl, -1, FALSE, NULL, path, _("Desktop"), FALSE, SHORTCUTS_DESKTOP);
   /* We do not actually pop up an error dialog if there is no desktop directory
    * because some people may really not want to have one.
    */
@@ -1424,8 +1655,8 @@ shortcuts_append_paths (GtkFileChooserDefault *impl,
       label = gtk_file_system_get_bookmark_label (impl->file_system, path);
 
       /* NULL GError, but we don't really want to show error boxes here */
-      if (shortcuts_insert_path (impl, start_row + num_inserted, FALSE, NULL, path, label, TRUE, NULL))
-       num_inserted++;
+      shortcuts_insert_path (impl, start_row + num_inserted, FALSE, NULL, path, label, TRUE, SHORTCUTS_BOOKMARKS);
+      num_inserted++;
 
       g_free (label);
     }
@@ -1490,30 +1721,6 @@ shortcuts_get_index (GtkFileChooserDefault *impl,
   return n;
 }
 
-/* Removes the specified number of rows from the shortcuts list */
-static void
-shortcuts_remove_rows (GtkFileChooserDefault *impl,
-                      int                    start_row,
-                      int                    n_rows)
-{
-  GtkTreePath *path;
-
-  path = gtk_tree_path_new_from_indices (start_row, -1);
-
-  for (; n_rows; n_rows--)
-    {
-      GtkTreeIter iter;
-
-      if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
-       g_assert_not_reached ();
-
-      shortcuts_free_row_data (impl, &iter);
-      gtk_list_store_remove (impl->shortcuts_model, &iter);
-    }
-
-  gtk_tree_path_free (path);
-}
-
 /* Adds all the file system volumes to the shortcuts model */
 static void
 shortcuts_add_volumes (GtkFileChooserDefault *impl)
@@ -1563,10 +1770,8 @@ shortcuts_add_volumes (GtkFileChooserDefault *impl)
            }
        }
 
-      if (shortcuts_insert_path (impl, start_row + n, TRUE, volume, NULL, NULL, FALSE, NULL))
-       n++;
-      else
-       gtk_file_system_volume_free (impl->file_system, volume);
+      shortcuts_insert_path (impl, start_row + n, TRUE, volume, NULL, NULL, FALSE, SHORTCUTS_VOLUMES);
+      n++;
     }
 
   impl->num_volumes = n;
@@ -1613,6 +1818,7 @@ shortcuts_add_bookmarks (GtkFileChooserDefault *impl)
 
   profile_start ("start", NULL);
         
+
   old_changing_folders = impl->changing_folder;
   impl->changing_folder = TRUE;
 
@@ -1647,8 +1853,10 @@ shortcuts_add_bookmarks (GtkFileChooserDefault *impl)
                           shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR),
                           impl->num_bookmarks + 1);
 
+  impl->num_bookmarks = 0;
+
   bookmarks = gtk_file_system_list_bookmarks (impl->file_system);
-  impl->num_bookmarks = shortcuts_append_paths (impl, bookmarks);
+  shortcuts_append_paths (impl, bookmarks);
   gtk_file_paths_free (bookmarks);
 
   if (impl->num_bookmarks > 0)
@@ -1715,26 +1923,15 @@ shortcuts_add_current_folder (GtkFileChooserDefault *impl)
       if (base_path &&
          strcmp (gtk_file_path_get_string (base_path), gtk_file_path_get_string (impl->current_folder)) == 0)
        {
-         success = shortcuts_insert_path (impl, pos, TRUE, volume, NULL, NULL, FALSE, NULL);
-         if (success)
-           volume = NULL;
+         shortcuts_insert_path (impl, pos, TRUE, volume, NULL, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER);
        }
       else
-       success = shortcuts_insert_path (impl, pos, FALSE, NULL, impl->current_folder, NULL, FALSE, NULL);
-
-      if (volume)
-       gtk_file_system_volume_free (impl->file_system, volume);
+       shortcuts_insert_path (impl, pos, FALSE, NULL, impl->current_folder, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER);
 
       if (base_path)
        gtk_file_path_free (base_path);
-
-      if (!success)
-       shortcuts_remove_rows (impl, pos - 1, 1); /* remove the separator */
-
-      impl->shortcuts_current_folder_active = success;
     }
-
-  if (success && impl->save_folder_combo != NULL)
+  else if (impl->save_folder_combo != NULL)
     gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), pos);
 }
 
@@ -1788,7 +1985,8 @@ shortcuts_model_create (GtkFileChooserDefault *impl)
                                              G_TYPE_POINTER,   /* path or volume */
                                              G_TYPE_BOOLEAN,   /* is the previous column a volume? */
                                              G_TYPE_BOOLEAN,   /* removable */
-                                             G_TYPE_BOOLEAN);  /* pixbuf cell visibility */
+                                             G_TYPE_BOOLEAN,   /* pixbuf cell visibility */
+                                             G_TYPE_OBJECT);   /* GtkFileSystemHandle */
 
   if (impl->file_system)
     {
@@ -1837,6 +2035,33 @@ new_folder_button_clicked (GtkButton             *button,
   gtk_tree_path_free (path);
 }
 
+static void
+edited_idle_create_folder_cb (GtkFileSystemHandle *handle,
+                             const GtkFilePath   *path,
+                             const GError        *error,
+                             gpointer             data)
+{
+  gboolean cancelled = handle->cancelled;
+  GtkFileChooserDefault *impl = data;
+
+  if (!g_slist_find (impl->pending_handles, handle))
+    goto out;
+
+  impl->pending_handles = g_slist_remove (impl->pending_handles, handle);
+
+  if (cancelled)
+    goto out;
+
+  if (!error)
+    change_folder_and_display_error (impl, path);
+  else
+    error_creating_folder_dialog (impl, path, g_error_copy (error));
+
+ out:
+  g_object_unref (impl);
+  g_object_unref (handle);
+}
+
 /* Idle handler for creating a new folder after editing its name cell, or for
  * canceling the editing.
  */
@@ -1859,15 +2084,18 @@ edited_idle_cb (GtkFileChooserDefault *impl)
       GtkFilePath *file_path;
 
       error = NULL;
-      file_path = gtk_file_system_make_path (impl->file_system, impl->current_folder, impl->edited_new_text,
+      file_path = gtk_file_system_make_path (impl->file_system,
+                                            impl->current_folder,
+                                            impl->edited_new_text,
                                             &error);
       if (file_path)
        {
-         error = NULL;
-         if (gtk_file_system_create_folder (impl->file_system, file_path, &error))
-           change_folder_and_display_error (impl, file_path);
-         else
-           error_creating_folder_dialog (impl, file_path, error);
+         GtkFileSystemHandle *handle;
+
+         handle = gtk_file_system_create_folder (impl->file_system, file_path,
+                                                 edited_idle_create_folder_cb,
+                                                 g_object_ref (impl));
+         impl->pending_handles = g_slist_append (impl->pending_handles, handle);
 
          gtk_file_path_free (file_path);
        }
@@ -1981,6 +2209,12 @@ shortcut_find_position (GtkFileChooserDefault *impl,
 
   current_folder_separator_idx = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
 
+#if 0
+  /* FIXME: is this still needed? */
+  if (current_folder_separator_idx >= impl->shortcuts_model->length)
+    return -1;
+#endif
+
   for (i = 0; i < current_folder_separator_idx; i++)
     {
       gpointer col_data;
@@ -3415,52 +3649,134 @@ error_selecting_dragged_file_dialog (GtkFileChooserDefault *impl,
 }
 
 static void
-file_list_drag_data_received_cb (GtkWidget          *widget,
-                                GdkDragContext     *context,
-                                gint                x,
-                                gint                y,
-                                GtkSelectionData   *selection_data,
-                                guint               info,
-                                guint               time_,
-                                gpointer            data)
+file_list_drag_data_select_uris (GtkFileChooserDefault  *impl,
+                                gchar                 **uris)
 {
-  GtkFileChooserDefault *impl;
-  GtkFileChooser *chooser;
-  gchar **uris;
+  int i;
   char *uri;
-  GtkFilePath *path;
-  GError *error = NULL;
-  gint i;
-  impl = GTK_FILE_CHOOSER_DEFAULT (data);
-  chooser = GTK_FILE_CHOOSER (data);
-  
-  /* Parse the text/uri-list string, navigate to the first one */
-  uris = g_uri_list_extract_uris ((const char *) selection_data->data);
-  if (uris[0]) 
+  GtkFileChooser *chooser = GTK_FILE_CHOOSER (impl);
+
+  for (i = 1; uris[i]; i++)
     {
-      uri = uris[0];
-      path = gtk_file_system_uri_to_path (impl->file_system, uri);
-      
+      GtkFilePath *path;
+
+      uri = uris[i];
+      path = gtk_file_system_uri_to_path (impl->file_system, uri);
+
       if (path)
-       {
-         if ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
-              impl->action == GTK_FILE_CHOOSER_ACTION_SAVE) &&
-             uris[1] == 0 &&
-             check_is_folder (impl->file_system, path, NULL))
-           change_folder_and_display_error (impl, path);
-          else
-            {
-              gtk_file_chooser_default_unselect_all (chooser);
-              gtk_file_chooser_default_select_path (chooser, path, &error);
-              if (error)
-               error_selecting_dragged_file_dialog (impl, path, error);
-             else
-               browse_files_center_selected_row (impl);
-            }
+        {
+         GError *error = NULL;
+
+         gtk_file_chooser_default_select_path (chooser, path, &error);
+         if (error)
+           error_selecting_dragged_file_dialog (impl, path, error);
 
          gtk_file_path_free (path);
        }
+    }
+}
+
+struct FileListDragData
+{
+  GtkFileChooserDefault *impl;
+  gchar **uris;
+  GtkFilePath *path;
+};
+
+static void
+file_list_drag_data_received_get_info_cb (GtkFileSystemHandle *handle,
+                                         const GtkFileInfo   *info,
+                                         const GError        *error,
+                                         gpointer             user_data)
+{
+  gboolean cancelled = handle->cancelled;
+  struct FileListDragData *data = user_data;
+  GtkFileChooser *chooser = GTK_FILE_CHOOSER (data->impl);
+
+  if (handle != data->impl->file_list_drag_data_received_handle)
+    goto out;
+
+  data->impl->file_list_drag_data_received_handle = NULL;
+
+  if (cancelled || error)
+    goto out;
+
+  if ((data->impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
+       data->impl->action == GTK_FILE_CHOOSER_ACTION_SAVE) &&
+      data->uris[1] == 0 && !error &&
+      gtk_file_info_get_is_folder (info))
+    change_folder_and_display_error (data->impl, data->path);
+  else
+    {
+      GError *error = NULL;
+
+      gtk_file_chooser_default_unselect_all (chooser);
+      gtk_file_chooser_default_select_path (chooser, data->path, &error);
+      if (error)
+       error_selecting_dragged_file_dialog (data->impl, data->path, error);
+      else
+       browse_files_center_selected_row (data->impl);
+    }
+
+  if (data->impl->select_multiple)
+    file_list_drag_data_select_uris (data->impl, data->uris);
+
+out:
+  g_object_unref (data->impl);
+  g_strfreev (data->uris);
+  gtk_file_path_free (data->path);
+  g_free (data);
+
+  g_object_unref (handle);
+}
+
+static void
+file_list_drag_data_received_cb (GtkWidget          *widget,
+                                GdkDragContext     *context,
+                                gint                x,
+                                gint                y,
+                                GtkSelectionData   *selection_data,
+                                guint               info,
+                                guint               time_,
+                                gpointer            data)
+{
+  GtkFileChooserDefault *impl;
+  GtkFileChooser *chooser;
+  gchar **uris;
+  char *uri;
+  GtkFilePath *path;
+  GError *error = NULL;
+  gint i;
+  impl = GTK_FILE_CHOOSER_DEFAULT (data);
+  chooser = GTK_FILE_CHOOSER (data);
+  
+  /* Parse the text/uri-list string, navigate to the first one */
+  uris = g_uri_list_extract_uris ((const char *) selection_data->data);
+  if (uris[0]) 
+    {
+      uri = uris[0];
+      path = gtk_file_system_uri_to_path (impl->file_system, uri);
+      
+      if (path)
+       {
+         struct FileListDragData *data;
+
+         data = g_new0 (struct FileListDragData, 1);
+         data->impl = g_object_ref (impl);
+         data->uris = uris;
+         data->path = path;
+
+         if (impl->file_list_drag_data_received_handle)
+           gtk_file_system_cancel_operation (impl->file_list_drag_data_received_handle);
+
+         impl->file_list_drag_data_received_handle =
+           gtk_file_system_get_info (impl->file_system, path,
+                                     GTK_FILE_INFO_IS_FOLDER,
+                                     file_list_drag_data_received_get_info_cb,
+                                     data);
+         goto out;
+       }
       else
        {
          g_set_error (&error,
@@ -3472,28 +3788,13 @@ file_list_drag_data_received_cb (GtkWidget          *widget,
          error_selecting_dragged_file_dialog (impl, NULL, error);
        }
 
-      
       if (impl->select_multiple)
-       {
-         for (i = 1; uris[i]; i++)
-           {
-             uri = uris[i];
-             path = gtk_file_system_uri_to_path (impl->file_system, uri);
-             
-             if (path)
-               {
-                 gtk_file_chooser_default_select_path (chooser, path, &error);
-                 if (error)
-                   error_selecting_dragged_file_dialog (impl, path, error);
-
-                 gtk_file_path_free (path);
-               }
-           }
-       }
+       file_list_drag_data_select_uris (impl, uris);
     }
 
   g_strfreev (uris);
 
+out:
   g_signal_stop_emission_by_name (widget, "drag-data-received");
 }
 
@@ -4518,6 +4819,7 @@ remove_settings_signal (GtkFileChooserDefault *impl,
 static void
 gtk_file_chooser_default_dispose (GObject *object)
 {
+  GSList *l;
   GtkFileChooserDefault *impl = (GtkFileChooserDefault *) object;
 
   if (impl->extra_widget)
@@ -4526,6 +4828,90 @@ gtk_file_chooser_default_dispose (GObject *object)
       impl->extra_widget = NULL;
     }
 
+  if (impl->volumes_changed_id > 0)
+    {
+      g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
+      impl->volumes_changed_id = 0;
+    }
+
+  if (impl->bookmarks_changed_id > 0)
+    {
+      g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
+      impl->bookmarks_changed_id = 0;
+    }
+
+  pending_select_paths_free (impl);
+
+  /* cancel all pending operations */
+  if (impl->pending_handles)
+    {
+      for (l = impl->pending_handles; l; l = l->next)
+        {
+         GtkFileSystemHandle *handle =l->data;
+         gtk_file_system_cancel_operation (handle);
+        }
+      g_slist_free (impl->pending_handles);
+      impl->pending_handles = NULL;
+    }
+
+  if (impl->reload_icon_handles)
+    {
+      for (l = impl->reload_icon_handles; l; l = l->next)
+        {
+         GtkFileSystemHandle *handle =l->data;
+         gtk_file_system_cancel_operation (handle);
+        }
+      g_slist_free (impl->reload_icon_handles);
+      impl->reload_icon_handles = NULL;
+    }
+
+  if (impl->loading_shortcuts)
+    {
+      for (l = impl->loading_shortcuts; l; l = l->next)
+        {
+         GtkFileSystemHandle *handle =l->data;
+         gtk_file_system_cancel_operation (handle);
+        }
+      g_slist_free (impl->loading_shortcuts);
+      impl->loading_shortcuts = NULL;
+    }
+
+  if (impl->file_list_drag_data_received_handle)
+    {
+      gtk_file_system_cancel_operation (impl->file_list_drag_data_received_handle);
+      impl->file_list_drag_data_received_handle = NULL;
+    }
+
+  if (impl->update_current_folder_handle)
+    {
+      gtk_file_system_cancel_operation (impl->update_current_folder_handle);
+      impl->update_current_folder_handle = NULL;
+    }
+
+  if (impl->show_and_select_paths_handle)
+    {
+      gtk_file_system_cancel_operation (impl->show_and_select_paths_handle);
+      impl->show_and_select_paths_handle = NULL;
+    }
+
+  if (impl->should_respond_get_info_handle)
+    {
+      gtk_file_system_cancel_operation (impl->should_respond_get_info_handle);
+      impl->should_respond_get_info_handle = NULL;
+    }
+
+  if (impl->update_from_entry_handle)
+    {
+      gtk_file_system_cancel_operation (impl->update_from_entry_handle);
+      impl->update_from_entry_handle = NULL;
+    }
+
+  if (impl->shortcuts_activate_iter_handle)
+    {
+      gtk_file_system_cancel_operation (impl->shortcuts_activate_iter_handle);
+      impl->shortcuts_activate_iter_handle = NULL;
+    }
+
   remove_settings_signal (impl, gtk_widget_get_screen (GTK_WIDGET (impl)));
 
   G_OBJECT_CLASS (parent_class)->dispose (object);
@@ -4777,11 +5163,12 @@ gtk_file_chooser_default_map (GtkWidget *widget)
       break;
 
     case RELOAD_WAS_UNMAPPED:
-      /* Just reload the current folder */
-      g_assert (impl->current_folder != NULL);
-
-      pending_select_paths_store_selection (impl);
-      change_folder_and_display_error (impl, impl->current_folder);
+      /* Just reload the current folder; else continue the pending load. */
+      if (impl->current_folder)
+        {
+          pending_select_paths_store_selection (impl);
+          change_folder_and_display_error (impl, impl->current_folder);
+       }
       break;
 
     default:
@@ -5092,36 +5479,25 @@ browse_files_center_selected_row (GtkFileChooserDefault *impl)
   gtk_tree_selection_selected_foreach (selection, center_selected_row_foreach_cb, &closure);
 }
 
-static gboolean
-show_and_select_paths (GtkFileChooserDefault *impl,
-                      const GtkFilePath     *parent_path,
-                      GSList                *paths,
-                      GError                **error)
+struct ShowAndSelectPathsData
+{
+  GtkFileChooserDefault *impl;
+  GSList *paths;
+};
+
+static void
+show_and_select_paths_finished_loading (GtkFileFolder *folder,
+                                       gpointer user_data)
 {
-  GtkFileFolder *folder;
   gboolean have_hidden;
   gboolean have_filtered;
   GSList *l;
-
-  profile_start ("start", NULL);
-
-  if (!paths)
-    {
-      profile_end ("end", NULL);
-      return TRUE;
-    }
-
-  folder = gtk_file_system_get_folder (impl->file_system, parent_path, GTK_FILE_INFO_IS_FOLDER | GTK_FILE_INFO_IS_HIDDEN, error);
-  if (!folder)
-    {
-      profile_end ("end", NULL);
-      return FALSE;
-    }
+  struct ShowAndSelectPathsData *data = user_data;
 
   have_hidden = FALSE;
   have_filtered = FALSE;
 
-  for (l = paths; l; l = l->next)
+  for (l = data->paths; l; l = l->next)
     {
       const GtkFilePath *path;
       GtkFileInfo *info;
@@ -5136,7 +5512,7 @@ show_and_select_paths (GtkFileChooserDefault *impl,
            have_hidden = gtk_file_info_get_is_hidden (info);
 
          if (!have_filtered)
-           have_filtered = !gtk_file_info_get_is_folder (info) && get_is_file_filtered (impl, path, info);
+           have_filtered = !gtk_file_info_get_is_folder (info) && get_is_file_filtered (data->impl, path, info);
 
          gtk_file_info_free (info);
 
@@ -5145,22 +5521,95 @@ show_and_select_paths (GtkFileChooserDefault *impl,
        }
     }
 
+  g_signal_handlers_disconnect_by_func (folder,
+                                       show_and_select_paths_finished_loading,
+                                       user_data);
+
   g_object_unref (folder);
 
   if (have_hidden)
-    g_object_set (impl, "show-hidden", TRUE, NULL);
+    g_object_set (data->impl, "show-hidden", TRUE, NULL);
 
   if (have_filtered)
-    set_current_filter (impl, NULL);
+    set_current_filter (data->impl, NULL);
 
-  for (l = paths; l; l = l->next)
+  for (l = data->paths; l; l = l->next)
     {
       const GtkFilePath *path;
 
       path = l->data;
-      _gtk_file_system_model_path_do (impl->browse_files_model, path, select_func, impl);
+      _gtk_file_system_model_path_do (data->impl->browse_files_model, path,
+                                     select_func, data->impl);
+    }
+
+  gtk_file_paths_free (data->paths);
+  g_free (data);
+}
+
+static void
+show_and_select_paths_get_folder_cb (GtkFileSystemHandle   *handle,
+                                    GtkFileFolder         *folder,
+                                    const GError          *error,
+                                    gpointer               user_data)
+{
+  gboolean cancelled = handle->cancelled;
+  struct ShowAndSelectPathsData *data = user_data;
+
+  if (data->impl->show_and_select_paths_handle != handle)
+    goto out;
+
+  data->impl->show_and_select_paths_handle = NULL;
+
+  if (cancelled || error)
+    goto out;
+
+  g_object_unref (handle);
+
+  if (gtk_file_folder_is_finished_loading (folder))
+    show_and_select_paths_finished_loading (folder, user_data);
+  else
+    g_signal_connect (folder, "finished-loading",
+                     G_CALLBACK (show_and_select_paths_finished_loading),
+                     user_data);
+
+  return;
+
+out:
+  g_object_unref (data->impl);
+  gtk_file_paths_free (data->paths);
+  g_free (data);
+
+  g_object_unref (handle);
+}
+
+static gboolean
+show_and_select_paths (GtkFileChooserDefault *impl,
+                      const GtkFilePath     *parent_path,
+                      GSList                *paths,
+                      GError                **error)
+{
+  struct ShowAndSelectPathsData *info;
+
+  profile_start ("start", NULL);
+
+  if (!paths)
+    {
+      profile_end ("end", NULL);
+      return TRUE;
     }
 
+  info = g_new (struct ShowAndSelectPathsData, 1);
+  info->impl = g_object_ref (impl);
+  info->paths = gtk_file_paths_copy (paths);
+
+  if (impl->show_and_select_paths_handle)
+    gtk_file_system_cancel_operation (impl->show_and_select_paths_handle);
+
+  impl->show_and_select_paths_handle =
+    gtk_file_system_get_folder (impl->file_system, parent_path,
+                               GTK_FILE_INFO_IS_FOLDER | GTK_FILE_INFO_IS_HIDDEN,
+                               show_and_select_paths_get_folder_cb, info);
+
   profile_end ("end", NULL);
   return TRUE;
 }
@@ -5343,50 +5792,52 @@ gtk_file_chooser_default_set_current_folder (GtkFileChooser    *chooser,
   return gtk_file_chooser_default_update_current_folder (chooser, path, FALSE, error);
 }
 
-static gboolean
-gtk_file_chooser_default_update_current_folder (GtkFileChooser    *chooser,
-                                               const GtkFilePath *path,
-                                               gboolean           keep_trail,
-                                               GError           **error)
+
+struct UpdateCurrentFolderData
 {
-  GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
-  gboolean result;
+  GtkFileChooserDefault *impl;
+  GtkFilePath *path;
+  gboolean keep_trail;
+};
 
-  profile_start ("start", (char *) path);
+static void
+update_current_folder_get_info_cb (GtkFileSystemHandle *handle,
+                                  const GtkFileInfo   *info,
+                                  const GError        *error,
+                                  gpointer             user_data)
+{
+  gboolean cancelled = handle->cancelled;
+  struct UpdateCurrentFolderData *data = user_data;
+  GtkFileChooserDefault *impl = data->impl;
 
-  g_assert (path != NULL);
+  if (handle != impl->update_current_folder_handle)
+    goto out;
 
-  if (impl->local_only &&
-      !gtk_file_system_path_is_local (impl->file_system, path))
-    {
-      g_set_error (error,
-                  GTK_FILE_CHOOSER_ERROR,
-                  GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
-                  _("Cannot change to folder because it is not local"));
+  impl->update_current_folder_handle = NULL;
 
-      profile_end ("end - not local", (char *) path);
-      return FALSE;
-    }
+  set_busy_cursor (impl, FALSE);
 
-  /* Test validity of path here.  */
-  if (!check_is_folder (impl->file_system, path, error))
-    {
-      profile_end ("end - not a folder", (char *) path);
-      return FALSE;
-    }
+  if (cancelled)
+    goto out;
 
-  if (!_gtk_path_bar_set_path (GTK_PATH_BAR (impl->browse_path_bar), path, keep_trail, error))
+  if (error)
     {
-      profile_end ("end - could not set path bar", (char *) path);
-      return FALSE;
+      error_changing_folder_dialog (impl, data->path, g_error_copy (error));
+      goto out;
     }
 
-  if (impl->current_folder != path)
+  if (!gtk_file_info_get_is_folder (info))
+    goto out;
+
+  if (!_gtk_path_bar_set_path (GTK_PATH_BAR (impl->browse_path_bar), data->path, data->keep_trail, NULL))
+    goto out;
+
+  if (impl->current_folder != data->path)
     {
       if (impl->current_folder)
        gtk_file_path_free (impl->current_folder);
 
-      impl->current_folder = gtk_file_path_copy (path);
+      impl->current_folder = gtk_file_path_copy (data->path);
 
       impl->reload_state = RELOAD_HAS_FOLDER;
     }
@@ -5412,7 +5863,7 @@ gtk_file_chooser_default_update_current_folder (GtkFileChooser    *chooser,
    * but perform more actions rather than returning immediately even if it
    * generates an error.
    */
-  result = set_list_model (impl, error);
+  set_list_model (impl, NULL);
 
   /* Refresh controls */
 
@@ -5425,8 +5876,56 @@ gtk_file_chooser_default_update_current_folder (GtkFileChooser    *chooser,
 
   g_signal_emit_by_name (impl, "selection-changed", 0);
 
+out:
+  gtk_file_path_free (data->path);
+  g_free (data);
+
+  g_object_unref (handle);
+}
+
+static gboolean
+gtk_file_chooser_default_update_current_folder (GtkFileChooser    *chooser,
+                                               const GtkFilePath *path,
+                                               gboolean           keep_trail,
+                                               GError           **error)
+{
+  GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
+  struct UpdateCurrentFolderData *data;
+
+  profile_start ("start", (char *) path);
+
+  g_assert (path != NULL);
+
+  if (impl->local_only &&
+      !gtk_file_system_path_is_local (impl->file_system, path))
+    {
+      g_set_error (error,
+                  GTK_FILE_CHOOSER_ERROR,
+                  GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
+                  _("Cannot change to folder because it is not local"));
+
+      profile_end ("end - not local", (char *) path);
+      return FALSE;
+    }
+
+  if (impl->update_current_folder_handle)
+    gtk_file_system_cancel_operation (impl->update_current_folder_handle);
+
+  /* Test validity of path here.  */
+  data = g_new0 (struct UpdateCurrentFolderData, 1);
+  data->impl = impl;
+  data->path = gtk_file_path_copy (path);
+  data->keep_trail = keep_trail;
+
+  impl->update_current_folder_handle =
+    gtk_file_system_get_info (impl->file_system, path, GTK_FILE_INFO_IS_FOLDER,
+                             update_current_folder_get_info_cb,
+                             data);
+
+  set_busy_cursor (impl, TRUE);
+
   profile_end ("end", NULL);
-  return result;
+  return TRUE;
 }
 
 static GtkFilePath *
@@ -5621,7 +6120,8 @@ check_save_entry (GtkFileChooserDefault *impl,
                  GtkFilePath          **path_ret,
                  gboolean              *is_well_formed_ret,
                  gboolean              *is_empty_ret,
-                 gboolean              *is_file_part_empty_ret)
+                 gboolean              *is_file_part_empty_ret,
+                 gboolean              *is_folder)
 {
   GtkFileChooserEntry *chooser_entry;
   const GtkFilePath *current_folder;
@@ -5640,6 +6140,7 @@ check_save_entry (GtkFileChooserDefault *impl,
       *is_well_formed_ret = TRUE;
       *is_empty_ret = TRUE;
       *is_file_part_empty_ret = TRUE;
+      *is_folder = FALSE;
 
       return;
     }
@@ -5654,6 +6155,7 @@ check_save_entry (GtkFileChooserDefault *impl,
       *path_ret = gtk_file_path_copy (current_folder);
       *is_well_formed_ret = TRUE;
       *is_file_part_empty_ret = TRUE;
+      *is_folder = TRUE;
 
       return;
     }
@@ -5668,12 +6170,14 @@ check_save_entry (GtkFileChooserDefault *impl,
       error_building_filename_dialog (impl, current_folder, file_part, error);
       *path_ret = NULL;
       *is_well_formed_ret = FALSE;
+      *is_folder = FALSE;
 
       return;
     }
 
   *path_ret = path;
   *is_well_formed_ret = TRUE;
+  *is_folder = _gtk_file_chooser_entry_get_is_folder (chooser_entry, path);
 }
 
 struct get_paths_closure {
@@ -5719,9 +6223,9 @@ gtk_file_chooser_default_get_paths (GtkFileChooser *chooser)
   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
       || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
     {
-      gboolean is_well_formed, is_empty, is_file_part_empty;
+      gboolean is_well_formed, is_empty, is_file_part_empty, is_folder;
 
-      check_save_entry (impl, &info.path_from_entry, &is_well_formed, &is_empty, &is_file_part_empty);
+      check_save_entry (impl, &info.path_from_entry, &is_well_formed, &is_empty, &is_file_part_empty, &is_folder);
 
       if (!is_well_formed)
        return NULL;
@@ -5872,19 +6376,53 @@ shortcuts_get_pos_for_shortcut_folder (GtkFileChooserDefault *impl,
   return pos + shortcuts_get_index (impl, SHORTCUTS_SHORTCUTS);
 }
 
+struct AddShortcutData
+{
+  GtkFileChooserDefault *impl;
+  GtkFilePath *path;
+};
+
+static void
+add_shortcut_get_info_cb (GtkFileSystemHandle *handle,
+                         const GtkFileInfo   *info,
+                         const GError        *error,
+                         gpointer             user_data)
+{
+  int pos;
+  gboolean cancelled = handle->cancelled;
+  struct AddShortcutData *data = user_data;
+
+  if (!g_slist_find (data->impl->loading_shortcuts, handle))
+    goto out;
+
+  data->impl->loading_shortcuts = g_slist_remove (data->impl->loading_shortcuts, handle);
+
+  if (cancelled || error || !gtk_file_info_get_is_folder (info))
+    goto out;
+
+  pos = shortcuts_get_pos_for_shortcut_folder (data->impl, data->impl->num_shortcuts);
+
+  shortcuts_insert_path (data->impl, pos, FALSE, NULL, data->path, NULL, FALSE, SHORTCUTS_SHORTCUTS);
+
+out:
+  g_object_unref (data->impl);
+  gtk_file_path_free (data->path);
+  g_free (data);
+
+  g_object_unref (handle);
+}
+
 static gboolean
 gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser    *chooser,
                                              const GtkFilePath *path,
                                              GError           **error)
 {
+  GtkFileSystemHandle *handle;
   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
-  gboolean result;
+  struct AddShortcutData *data;
+  GSList *l;
   int pos;
 
-  /* Test validity of path here.  */
-  if (!check_is_folder (impl->file_system, path, error))
-    return FALSE;
-
   /* Avoid adding duplicates */
   pos = shortcut_find_position (impl, path);
   if (pos >= 0 && pos < shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR))
@@ -5902,17 +6440,43 @@ gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser    *chooser,
       return FALSE;
     }
 
-  pos = shortcuts_get_pos_for_shortcut_folder (impl, impl->num_shortcuts);
+  for (l = impl->loading_shortcuts; l; l = l->next)
+    {
+      GtkFileSystemHandle *h = l->data;
+      GtkFilePath *p;
 
-  result = shortcuts_insert_path (impl, pos, FALSE, NULL, path, NULL, FALSE, error);
+      p = g_object_get_data (G_OBJECT (h), "add-shortcut-path-key");
+      if (p && !gtk_file_path_compare (path, p))
+        {
+         gchar *uri;
 
-  if (result)
-    impl->num_shortcuts++;
+          uri = gtk_file_system_path_to_uri (impl->file_system, path);
+          g_set_error (error,
+                      GTK_FILE_CHOOSER_ERROR,
+                      GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS,
+                      _("shortcut %s already exists"),
+                      uri);
+          g_free (uri);
 
-  if (impl->shortcuts_filter_model)
-    gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
+          return FALSE;
+       }
+    }
 
-  return result;
+  data = g_new0 (struct AddShortcutData, 1);
+  data->impl = g_object_ref (impl);
+  data->path = gtk_file_path_copy (path);
+
+  handle = gtk_file_system_get_info (impl->file_system, path,
+                                    GTK_FILE_INFO_IS_FOLDER,
+                                    add_shortcut_get_info_cb, data);
+
+  if (!handle)
+    return FALSE;
+
+  impl->loading_shortcuts = g_slist_append (impl->loading_shortcuts, handle);
+  g_object_set_data (G_OBJECT (handle), "add-shortcut-path-key", data->path);
+
+  return TRUE;
 }
 
 static gboolean
@@ -5923,9 +6487,24 @@ gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser    *chooser,
   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
   int pos;
   GtkTreeIter iter;
+  GSList *l;
   char *uri;
   int i;
 
+  for (l = impl->loading_shortcuts; l; l = l->next)
+    {
+      GtkFileSystemHandle *h = l->data;
+      GtkFilePath *p;
+
+      p = g_object_get_data (G_OBJECT (h), "add-shortcut-path-key");
+      if (p && !gtk_file_path_compare (path, p))
+        {
+         impl->loading_shortcuts = g_slist_remove (impl->loading_shortcuts, h);
+         gtk_file_system_cancel_operation (h);
+          return TRUE;
+       }
+    }
+
   if (impl->num_shortcuts == 0)
     goto out;
 
@@ -6240,48 +6819,46 @@ confirm_dialog_should_accept_filename (GtkFileChooserDefault *impl,
   return (response == GTK_RESPONSE_ACCEPT);
 }
 
-static char *
-get_display_name_for_folder (GtkFileChooserDefault *impl,
-                            const GtkFilePath     *path)
+struct GetDisplayNameData
 {
-  char *display_name;
-  GtkFilePath *parent_path;
-  GtkFileFolder *parent_folder;
-  GtkFileInfo *info;
+  GtkFileChooserDefault *impl;
+  gchar *file_part;
+};
 
-  display_name = NULL;
-  parent_path = NULL;
-  parent_folder = NULL;
-  info = NULL;
+static void
+confirmation_confirm_get_info_cb (GtkFileSystemHandle *handle,
+                                 const GtkFileInfo   *info,
+                                 const GError        *error,
+                                 gpointer             user_data)
+{
+  gboolean cancelled = handle->cancelled;
+  gboolean should_respond = FALSE;
+  struct GetDisplayNameData *data = user_data;
 
-  if (!gtk_file_system_get_parent (impl->file_system, path, &parent_path, NULL))
+  if (handle != data->impl->should_respond_get_info_handle)
     goto out;
 
-  parent_folder = gtk_file_system_get_folder (impl->file_system,
-                                             parent_path ? parent_path : path,
-                                             GTK_FILE_INFO_DISPLAY_NAME,
-                                             NULL);
-  if (!parent_folder)
-    goto out;
+  data->impl->should_respond_get_info_handle = NULL;
 
-  info = gtk_file_folder_get_info (parent_folder, parent_path ? path : NULL, NULL);
-  if (!info)
+  if (cancelled)
     goto out;
 
-  display_name = g_strdup (gtk_file_info_get_display_name (info));
-
- out:
-
-  if (parent_path)
-    gtk_file_path_free (parent_path);
+  if (error)
+    /* Huh?  Did the folder disappear?  Let the caller deal with it */
+    should_respond = TRUE;
+  else
+    should_respond = confirm_dialog_should_accept_filename (data->impl, data->file_part, gtk_file_info_get_display_name (info));
 
-  if (parent_folder)
-    g_object_unref (parent_folder);
+  set_busy_cursor (data->impl, FALSE);
+  if (should_respond)
+    g_signal_emit_by_name (data->impl, "response-requested");
 
-  if (info)
-    gtk_file_info_free (info);
+out:
+  g_object_unref (data->impl);
+  g_free (data->file_part);
+  g_free (data);
 
-  return display_name;
+  g_object_unref (handle);
 }
 
 /* Does overwrite confirmation if appropriate, and returns whether the dialog
@@ -6305,18 +6882,24 @@ should_respond_after_confirm_overwrite (GtkFileChooserDefault *impl,
     {
     case GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM:
       {
-       char *parent_display_name;
-       gboolean retval;
+       struct GetDisplayNameData *data;
 
        g_assert (file_part != NULL);
 
-       parent_display_name = get_display_name_for_folder (impl, parent_path);
-       if (!parent_display_name)
-         return TRUE; /* Huh?  Did the folder disappear?  Let the caller deal with it */
+       data = g_new0 (struct GetDisplayNameData, 1);
+       data->impl = g_object_ref (impl);
+       data->file_part = g_strdup (file_part);
+
+       if (impl->should_respond_get_info_handle)
+         gtk_file_system_cancel_operation (impl->should_respond_get_info_handle);
 
-       retval = confirm_dialog_should_accept_filename (impl, file_part, parent_display_name);
-       g_free (parent_display_name);
-       return retval;
+       impl->should_respond_get_info_handle =
+         gtk_file_system_get_info (impl->file_system, parent_path,
+                                   GTK_FILE_INFO_DISPLAY_NAME,
+                                   confirmation_confirm_get_info_cb,
+                                   data);
+       set_busy_cursor (data->impl, TRUE);
+       return FALSE;
       }
 
     case GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME:
@@ -6331,6 +6914,114 @@ should_respond_after_confirm_overwrite (GtkFileChooserDefault *impl,
     }
 }
 
+static void
+action_create_folder_cb (GtkFileSystemHandle *handle,
+                        const GtkFilePath   *path,
+                        const GError        *error,
+                        gpointer             user_data)
+{
+  gboolean cancelled = handle->cancelled;
+  GtkFileChooserDefault *impl = user_data;
+
+  if (!g_slist_find (impl->pending_handles, handle))
+    goto out;
+
+  impl->pending_handles = g_slist_remove (impl->pending_handles, handle);
+
+  set_busy_cursor (impl, FALSE);
+
+  if (cancelled)
+    goto out;
+
+  if (error)
+    error_creating_folder_dialog (impl, path, g_error_copy (error));
+  else
+    g_signal_emit_by_name (impl, "response-requested");
+
+out:
+  g_object_unref (impl);
+  g_object_unref (handle);
+}
+
+struct SaveEntryData
+{
+  GtkFileChooserDefault *impl;
+  gboolean file_exists_and_is_not_folder;
+  GtkFilePath *parent_path;
+  GtkFilePath *path;
+};
+
+static void
+save_entry_get_info_cb (GtkFileSystemHandle *handle,
+                       const GtkFileInfo   *info,
+                       const GError        *error,
+                       gpointer             user_data)
+{
+  gboolean parent_is_folder;
+  gboolean cancelled = handle->cancelled;
+  struct SaveEntryData *data = user_data;
+
+  if (handle != data->impl->should_respond_get_info_handle)
+    goto out;
+
+  data->impl->should_respond_get_info_handle = NULL;
+
+  set_busy_cursor (data->impl, FALSE);
+
+  if (cancelled)
+    goto out;
+
+  if (!info)
+    parent_is_folder = FALSE;
+  else
+    parent_is_folder = gtk_file_info_get_is_folder (info);
+
+  if (parent_is_folder)
+    {
+      if (data->impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
+        {
+          if (data->file_exists_and_is_not_folder)
+           {
+             gboolean retval;
+             const char *file_part;
+
+             file_part = _gtk_file_chooser_entry_get_file_part (GTK_FILE_CHOOSER_ENTRY (data->impl->save_file_name_entry));
+             retval = should_respond_after_confirm_overwrite (data->impl, file_part, data->parent_path);
+
+             if (retval)
+               g_signal_emit_by_name (data->impl, "response-requested");
+           }
+         else
+           g_signal_emit_by_name (data->impl, "response-requested");
+       }
+      else /* GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER */
+        {
+         GtkFileSystemHandle *handle;
+
+         g_object_ref (data->impl);
+         handle = gtk_file_system_create_folder (data->impl->file_system,
+                                                 data->path,
+                                                 action_create_folder_cb,
+                                                 data->impl);
+         data->impl->pending_handles = g_slist_append (data->impl->pending_handles, handle);
+         set_busy_cursor (data->impl, TRUE);
+        }
+    }
+  else
+    {
+      /* This will display an error, which is what we want */
+      change_folder_and_display_error (data->impl, data->parent_path);
+    }
+
+out:
+  g_object_unref (data->impl);
+  gtk_file_path_free (data->path);
+  gtk_file_path_free (data->parent_path);
+  g_free (data);
+
+  g_object_unref (handle);
+}
+
 /* Implementation for GtkFileChooserEmbed::should_respond() */
 static gboolean
 gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
@@ -6437,7 +7128,7 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
                || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
 
       entry = GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry);
-      check_save_entry (impl, &path, &is_well_formed, &is_empty, &is_file_part_empty);
+      check_save_entry (impl, &path, &is_well_formed, &is_empty, &is_file_part_empty, &is_folder);
 
       if (is_empty || !is_well_formed)
        return FALSE;
@@ -6445,7 +7136,6 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
       g_assert (path != NULL);
 
       error = NULL;
-      is_folder = check_is_folder (impl->file_system, path, &error);
       if (is_folder)
        {
          if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
@@ -6478,48 +7168,29 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
          else
            {
              GtkFilePath *parent_path;
-             gboolean parent_is_folder;
+             struct SaveEntryData *data;
 
              /* check that everything up to the last component exists */
 
              parent_path = gtk_file_path_copy (_gtk_file_chooser_entry_get_current_folder (entry));
-             parent_is_folder = check_is_folder (impl->file_system, parent_path, NULL);
-             if (parent_is_folder)
-               {
-                 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
-                   {
-                     if (file_exists_and_is_not_folder)
-                       {
-                         const char *file_part;
-
-                         file_part = _gtk_file_chooser_entry_get_file_part (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry));
-                         retval = should_respond_after_confirm_overwrite (impl, file_part, parent_path);
-                       }
-                     else
-                       retval = TRUE;
-                   }
-                 else /* GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER */
-                   {
-                     GError *create_error;
-
-                     create_error = NULL;
-                     if (gtk_file_system_create_folder (impl->file_system, path, &create_error))
-                       retval = TRUE;
-                     else
-                       {
-                         error_creating_folder_dialog (impl, path, create_error);
-                         retval = FALSE;
-                       }
-                   }
-               }
-             else
-               {
-                 /* This will display an error, which is what we want */
-                 change_folder_and_display_error (impl, parent_path);
-                 retval = FALSE;
-               }
 
-             gtk_file_path_free (parent_path);
+             data = g_new0 (struct SaveEntryData, 1);
+             data->impl = g_object_ref (impl);
+             data->file_exists_and_is_not_folder = file_exists_and_is_not_folder;
+             data->parent_path = parent_path; /* Takes ownership */
+             data->path = gtk_file_path_copy (path);
+
+             if (impl->should_respond_get_info_handle)
+               gtk_file_system_cancel_operation (impl->should_respond_get_info_handle);
+
+             impl->should_respond_get_info_handle =
+               gtk_file_system_get_info (impl->file_system, parent_path,
+                                         GTK_FILE_INFO_IS_FOLDER,
+                                         save_entry_get_info_cb,
+                                         data);
+             set_busy_cursor (impl, TRUE);
+
+             retval = FALSE;
            }
 
          if (error != NULL)
@@ -6691,6 +7362,51 @@ check_preview_change (GtkFileChooserDefault *impl)
     }
 }
 
+static void
+shortcuts_activate_volume_mount_cb (GtkFileSystemHandle *handle,
+                                   GtkFileSystemVolume *volume,
+                                   const GError        *error,
+                                   gpointer             data)
+{
+  GtkFilePath *path;
+  gboolean cancelled = handle->cancelled;
+  GtkFileChooserDefault *impl = data;
+
+  if (handle != impl->shortcuts_activate_iter_handle)
+    goto out;
+
+  impl->shortcuts_activate_iter_handle = NULL;
+
+  set_busy_cursor (impl, FALSE);
+
+  if (cancelled)
+    goto out;
+
+  if (error)
+    {
+      char *msg;
+
+      msg = g_strdup_printf (_("Could not mount %s"),
+                            gtk_file_system_volume_get_display_name (impl->file_system, volume));
+      error_message (impl, msg, error->message);
+      g_free (msg);
+
+      goto out;
+    }
+
+  path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
+  if (path != NULL)
+    {
+      change_folder_and_display_error (impl, path);
+      gtk_file_path_free (path);
+    }
+
+out:
+  g_object_unref (impl);
+  g_object_unref (handle);
+}
+
+
 /* Activates a volume by mounting it if necessary and then switching to its
  * base path.
  */
@@ -6707,44 +7423,63 @@ shortcuts_activate_volume (GtkFileChooserDefault *impl,
 
   if (!gtk_file_system_volume_get_is_mounted (impl->file_system, volume))
     {
-      GError *error;
-      gboolean result;
-
       set_busy_cursor (impl, TRUE);
 
-      error = NULL;
-      result = gtk_file_system_volume_mount (impl->file_system, volume, &error);
+      impl->shortcuts_activate_iter_handle =
+        gtk_file_system_volume_mount (impl->file_system, volume,
+                                     shortcuts_activate_volume_mount_cb,
+                                     g_object_ref (impl));
+    }
+  else
+    {
+      path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
+      if (path != NULL)
+        {
+          change_folder_and_display_error (impl, path);
+          gtk_file_path_free (path);
+        }
+    }
 
-      if (!result)
-       {
-         char *msg;
+  g_object_unref (impl);
+}
 
-         msg = g_strdup_printf (_("Could not mount %s"),
-                                gtk_file_system_volume_get_display_name (impl->file_system, volume));
-         error_message (impl, msg, error->message);
-         g_free (msg);
-         g_error_free (error);
-       }
+/* Opens the folder or volume at the specified iter in the shortcuts model */
+struct ShortcutsActivateData
+{
+  GtkFileChooserDefault *impl;
+  GtkFilePath *path;
+};
 
-      set_busy_cursor (impl, FALSE);
+static void
+shortcuts_activate_get_info_cb (GtkFileSystemHandle *handle,
+                               const GtkFileInfo   *info,
+                               const GError        *error,
+                               gpointer             user_data)
+{
+  gboolean cancelled = handle->cancelled;
+  struct ShortcutsActivateData *data = user_data;
 
-      if (!result)
-       goto out;
-    }
+  if (handle != data->impl->shortcuts_activate_iter_handle)
+    goto out;
 
-  path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
-  if (path != NULL)
-    {
-      change_folder_and_display_error (impl, path);
-      gtk_file_path_free (path);
-    }
+  data->impl->shortcuts_activate_iter_handle = NULL;
 
- out:
+  if (cancelled)
+    goto out;
 
-  g_object_unref (impl);
+  if (!error && gtk_file_info_get_is_folder (info))
+    change_folder_and_display_error (data->impl, data->path);
+  else
+    gtk_file_chooser_default_select_path (GTK_FILE_CHOOSER (data->impl), data->path, NULL);
+
+out:
+  g_object_unref (data->impl);
+  gtk_file_path_free (data->path);
+  g_free (data);
+
+  g_object_unref (handle);
 }
 
-/* Opens the folder or volume at the specified iter in the shortcuts model */
 static void
 shortcuts_activate_iter (GtkFileChooserDefault *impl,
                         GtkTreeIter           *iter)
@@ -6760,6 +7495,12 @@ shortcuts_activate_iter (GtkFileChooserDefault *impl,
   if (!col_data)
     return; /* We are on a separator */
 
+  if (impl->shortcuts_activate_iter_handle)
+    {
+      gtk_file_system_cancel_operation (impl->shortcuts_activate_iter_handle);
+      impl->shortcuts_activate_iter_handle = NULL;
+    }
+
   if (is_volume)
     {
       GtkFileSystemVolume *volume;
@@ -6770,13 +7511,16 @@ shortcuts_activate_iter (GtkFileChooserDefault *impl,
     }
   else
     {
-      const GtkFilePath *file_path;
+      struct ShortcutsActivateData *data;
 
-      file_path = col_data;
-      if (check_is_folder (impl->file_system, file_path, NULL))
-       change_folder_and_display_error (impl, file_path);
-      else
-       gtk_file_chooser_default_select_path (GTK_FILE_CHOOSER (impl), file_path, NULL);
+      data = g_new0 (struct ShortcutsActivateData, 1);
+      data->impl = g_object_ref (impl);
+      data->path = gtk_file_path_copy (col_data);
+
+      impl->shortcuts_activate_iter_handle =
+        gtk_file_system_get_info (impl->file_system, data->path,
+                                 GTK_FILE_INFO_IS_FOLDER,
+                                 shortcuts_activate_get_info_cb, data);
     }
 }
 
@@ -6989,9 +7733,14 @@ list_icon_data_func (GtkTreeViewColumn *tree_column,
 
   if (path)
     {
-      /* FIXME: NULL GError */
-      pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
-                                           impl->icon_size, NULL);
+      pixbuf = NULL;
+
+      if (info)
+        {
+          /* FIXME: NULL GError */
+         pixbuf = gtk_file_info_render_icon (info, GTK_WIDGET (impl),
+                                             impl->icon_size, NULL);
+       }
     }
   else
     {
@@ -7199,6 +7948,75 @@ location_entry_create (GtkFileChooserDefault *impl,
   return GTK_WIDGET (entry);
 }
 
+struct UpdateFromEntryData
+{
+  GtkFileChooserDefault *impl;
+  GtkFilePath *subfolder_path;
+  GtkFilePath *folder_path;
+  char *file_part;
+};
+
+static void
+update_from_entry_get_info_cb (GtkFileSystemHandle *handle,
+                              const GtkFileInfo   *file_info,
+                              const GError        *error,
+                              gpointer             user_data)
+{
+  gboolean cancelled = handle->cancelled;
+  struct UpdateFromEntryData *data = user_data;
+
+  if (handle != data->impl->update_from_entry_handle)
+    goto out;
+
+  data->impl->update_from_entry_handle = NULL;
+
+  if (cancelled)
+    goto out;
+
+  if (!file_info)
+    {
+      if (data->impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
+          || data->impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
+        {
+          if (!change_folder_and_display_error (data->impl, data->folder_path))
+            goto out;
+
+          gtk_file_chooser_default_set_current_name (GTK_FILE_CHOOSER (data->impl), data->file_part);
+        }
+      else
+        {
+         GError *err = g_error_copy (error);
+
+          error_getting_info_dialog (data->impl, data->subfolder_path, err);
+       }
+
+      goto out;
+    }
+
+  if (gtk_file_info_get_is_folder (file_info))
+    change_folder_and_display_error (data->impl, data->subfolder_path);
+  else
+    {
+      gboolean result;
+      GError *select_error = NULL;
+
+      result = _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (data->impl), data->subfolder_path, &select_error);
+
+      if (!result)
+        error_dialog (data->impl, _("Could not select item"),
+                     data->subfolder_path, select_error);
+    }
+
+out:
+  g_object_unref (data->impl);
+  gtk_file_path_free (data->subfolder_path);
+  gtk_file_path_free (data->folder_path);
+  g_free (data->file_part);
+  g_free (data);
+
+  g_object_unref (handle);
+}
+
 static gboolean
 update_from_entry (GtkFileChooserDefault *impl,
                   GtkWindow             *parent,
@@ -7222,29 +8040,16 @@ update_from_entry (GtkFileChooserDefault *impl,
     return change_folder_and_display_error (impl, folder_path);
   else
     {
-      GtkFileFolder *folder = NULL;
       GtkFilePath *subfolder_path = NULL;
-      GtkFileInfo *info = NULL;
-      GError *error;
-      gboolean result;
-
-      result = FALSE;
+      GError *error = NULL;
+      gboolean result = FALSE;
+      struct UpdateFromEntryData *data;
 
       /* If the file part is non-empty, we need to figure out if it refers to a
        * folder within folder. We could optimize the case here where the folder
        * is already loaded for one of our tree models.
        */
 
-      error = NULL;
-      folder = gtk_file_system_get_folder (impl->file_system, folder_path, GTK_FILE_INFO_IS_FOLDER, &error);
-
-      if (!folder)
-       {
-         error_getting_info_dialog (impl, folder_path, error);
-         goto out;
-       }
-
-      error = NULL;
       subfolder_path = gtk_file_system_make_path (impl->file_system, folder_path, file_part, &error);
 
       if (!subfolder_path)
@@ -7256,54 +8061,29 @@ update_from_entry (GtkFileChooserDefault *impl,
          msg = g_strdup_printf (_("Could not build file name from '%s' and '%s'"),
                                 uri, file_part);
          error_message (impl, msg, error->message);
+
          g_free (uri);
          g_free (msg);
-         goto out;
-       }
+          gtk_file_path_free (subfolder_path);
 
-      error = NULL;
-      info = gtk_file_folder_get_info (folder, subfolder_path, &error);
-
-      if (!info)
-       {
-         if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
-             || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
-           {
-             if (!change_folder_and_display_error (impl, folder_path))
-               goto out;
-
-             gtk_file_chooser_default_set_current_name (GTK_FILE_CHOOSER (impl), file_part);
-           }
-         else
-           error_getting_info_dialog (impl, subfolder_path, error);
-
-         goto out;
-       }
-
-      if (gtk_file_info_get_is_folder (info))
-       result = change_folder_and_display_error (impl, subfolder_path);
-      else
-       {
-         GError *error;
-
-         error = NULL;
-         result = _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (impl), subfolder_path, &error);
-         if (!result)
-           error_dialog (impl, _("Could not select item"),
-                         subfolder_path, error);
+          return result;
        }
 
-    out:
+      data = g_new0 (struct UpdateFromEntryData, 1);
+      data->impl = g_object_ref (impl);
+      data->folder_path = gtk_file_path_copy (folder_path);
+      data->subfolder_path = subfolder_path;
+      data->file_part = g_strdup (file_part);
 
-      if (folder)
-       g_object_unref (folder);
+      if (impl->update_from_entry_handle)
+       gtk_file_system_cancel_operation (impl->update_from_entry_handle);
 
-      gtk_file_path_free (subfolder_path);
+      impl->update_from_entry_handle =
+        gtk_file_system_get_info (impl->file_system, subfolder_path,
+                                 GTK_FILE_INFO_IS_FOLDER,
+                                 update_from_entry_get_info_cb, data);
 
-      if (info)
-       gtk_file_info_free (info);
-
-      return result;
+      return TRUE;
     }
 
   g_assert_not_reached ();
index 32a9fa9ce3bb9770ae1e57855008f8971a4b1eff..9db98970a21250c19e94d01d478e550d2f05a76a 100644 (file)
@@ -129,6 +129,7 @@ gtk_file_chooser_dialog_init (GtkFileChooserDialog *dialog)
   dialog->priv->default_height = -1;
   dialog->priv->resize_horizontally = TRUE;
   dialog->priv->resize_vertically = TRUE;
+  dialog->priv->response_requested = FALSE;
 
   gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
 
@@ -355,6 +356,39 @@ file_chooser_widget_default_size_changed (GtkWidget            *widget,
   else
     file_chooser_widget_default_unrealized_size_changed (widget, dialog);
 }
+
+static void
+file_chooser_widget_response_requested (GtkWidget            *widget,
+                                       GtkFileChooserDialog *dialog)
+{
+  GList *children, *l;
+
+  /* There probably isn't a default widget, so make things easier for the
+   * programmer by looking for a reasonable button on our own.
+   */
+
+  children = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area));
+
+  for (l = children; l; l = l->next)
+    {
+      GtkWidget *widget;
+      int response_id;
+
+      widget = GTK_WIDGET (l->data);
+      response_id = gtk_dialog_get_response_for_widget (GTK_DIALOG (dialog), widget);
+      if (response_id == GTK_RESPONSE_ACCEPT
+         || response_id == GTK_RESPONSE_OK
+         || response_id == GTK_RESPONSE_YES
+         || response_id == GTK_RESPONSE_APPLY)
+       {
+         dialog->priv->response_requested = TRUE;
+         gtk_widget_activate (widget); /* Should we gtk_dialog_response (dialog, response_id) instead? */
+         break;
+       }
+    }
+
+  g_list_free (children);
+}
   
 static GObject*
 gtk_file_chooser_dialog_constructor (GType                  type,
@@ -382,6 +416,8 @@ gtk_file_chooser_dialog_constructor (GType                  type,
                    G_CALLBACK (file_chooser_widget_file_activated), object);
   g_signal_connect (priv->widget, "default-size-changed",
                    G_CALLBACK (file_chooser_widget_default_size_changed), object);
+  g_signal_connect (priv->widget, "response-requested",
+                   G_CALLBACK (file_chooser_widget_response_requested), object);
 
   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (object)->vbox), priv->widget, TRUE, TRUE, 0);
 
@@ -550,8 +586,11 @@ response_cb (GtkDialog *dialog,
        || response_id == GTK_RESPONSE_APPLY))
     return;
 
-  if (!_gtk_file_chooser_embed_should_respond (GTK_FILE_CHOOSER_EMBED (priv->widget)))
-    g_signal_stop_emission_by_name (dialog, "response");
+  if (!priv->response_requested && !_gtk_file_chooser_embed_should_respond (GTK_FILE_CHOOSER_EMBED (priv->widget)))
+    {
+      g_signal_stop_emission_by_name (dialog, "response");
+      priv->response_requested = FALSE;
+    }
 }
 
 static GtkWidget *
index 1f584a3d8702cce499a64171d7bbdbf18c64dab6..8871fb7adbaa5f2f3174c2e8502361c9a116d8ae 100644 (file)
@@ -35,6 +35,8 @@ static gboolean delegate_should_respond       (GtkFileChooserEmbed *chooser_embe
 static void delegate_initial_focus            (GtkFileChooserEmbed *chooser_embed);
 static void delegate_default_size_changed     (GtkFileChooserEmbed *chooser_embed,
                                               gpointer             data);
+static void delegate_response_requested       (GtkFileChooserEmbed *chooser_embed,
+                                              gpointer             data);
 
 static GtkFileChooserEmbed *
 get_delegate (GtkFileChooserEmbed *receiver)
@@ -81,6 +83,8 @@ _gtk_file_chooser_embed_set_delegate (GtkFileChooserEmbed *receiver,
 
   g_signal_connect (delegate, "default_size_changed",
                    G_CALLBACK (delegate_default_size_changed), receiver);
+  g_signal_connect (delegate, "response_requested",
+                   G_CALLBACK (delegate_response_requested), receiver);
 }
 
 
@@ -120,6 +124,13 @@ delegate_default_size_changed (GtkFileChooserEmbed *chooser_embed,
   g_signal_emit_by_name (data, "default-size-changed");
 }
 
+static void
+delegate_response_requested (GtkFileChooserEmbed *chooser_embed,
+                            gpointer             data)
+{
+  g_signal_emit_by_name (data, "response-requested");
+}
+
 
 /* publicly callable functions */
 
@@ -160,6 +171,13 @@ gtk_file_chooser_embed_class_init (gpointer g_iface)
                NULL, NULL,
                _gtk_marshal_VOID__VOID,
                G_TYPE_NONE, 0);
+  g_signal_new (_("response-requested"),
+               iface_type,
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (GtkFileChooserEmbedIface, response_requested),
+               NULL, NULL,
+               _gtk_marshal_VOID__VOID,
+               G_TYPE_NONE, 0);
 }
 
 void
index 98b0ae54e9d8a45a78d1a6f8d45cc812bc58ea52..9477da0687f9373d45ed6211e0b2149fc3975458 100644 (file)
@@ -53,6 +53,7 @@ struct _GtkFileChooserEmbedIface
   /* Signals
    */
   void (*default_size_changed)    (GtkFileChooserEmbed *chooser_embed);
+  void (*response_requested)      (GtkFileChooserEmbed *chooser_embed);
 };
 
 GType _gtk_file_chooser_embed_get_type (void) G_GNUC_CONST;
index acbd1206c27038a06b2f41ad12aa529ecdf7685a..cbae54c121f906a609243df14c91fbf936caa9de 100644 (file)
@@ -55,6 +55,7 @@ struct _GtkFileChooserEntry
   GSource *load_directory_idle;
 
   GtkFileFolder *current_folder;
+  GtkFileSystemHandle *load_folder_handle;
 
   GtkListStore *completion_store;
 
@@ -75,6 +76,7 @@ static void gtk_file_chooser_entry_iface_init (GtkEditableClass         *iface);
 static void gtk_file_chooser_entry_init       (GtkFileChooserEntry      *chooser_entry);
 
 static void     gtk_file_chooser_entry_finalize       (GObject          *object);
+static void     gtk_file_chooser_entry_dispose        (GObject          *object);
 static gboolean gtk_file_chooser_entry_focus          (GtkWidget        *widget,
                                                       GtkDirectionType  direction);
 static void     gtk_file_chooser_entry_activate       (GtkEntry         *entry);
@@ -156,6 +158,7 @@ gtk_file_chooser_entry_class_init (GtkFileChooserEntryClass *class)
   parent_class = g_type_class_peek_parent (class);
 
   gobject_class->finalize = gtk_file_chooser_entry_finalize;
+  gobject_class->dispose = gtk_file_chooser_entry_dispose;
 
   widget_class->focus = gtk_file_chooser_entry_focus;
 
@@ -211,8 +214,29 @@ gtk_file_chooser_entry_finalize (GObject *object)
 {
   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (object);
 
+  gtk_file_path_free (chooser_entry->base_folder);
+  gtk_file_path_free (chooser_entry->current_folder_path);
+  g_free (chooser_entry->file_part);
+
+  parent_class->finalize (object);
+}
+
+static void
+gtk_file_chooser_entry_dispose (GObject *object)
+{
+  GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (object);
+
   if (chooser_entry->completion_store)
-    g_object_unref (chooser_entry->completion_store);
+    {
+      g_object_unref (chooser_entry->completion_store);
+      chooser_entry->completion_store = NULL;
+    }
+
+  if (chooser_entry->load_folder_handle)
+    {
+      gtk_file_system_cancel_operation (chooser_entry->load_folder_handle);
+      chooser_entry->load_folder_handle = NULL;
+    }
 
   if (chooser_entry->current_folder)
     {
@@ -221,16 +245,16 @@ gtk_file_chooser_entry_finalize (GObject *object)
       g_signal_handlers_disconnect_by_func (chooser_entry->current_folder,
                                            G_CALLBACK (files_deleted_cb), chooser_entry);
       g_object_unref (chooser_entry->current_folder);
+      chooser_entry->current_folder = NULL;
     }
 
   if (chooser_entry->file_system)
-    g_object_unref (chooser_entry->file_system);
-
-  gtk_file_path_free (chooser_entry->base_folder);
-  gtk_file_path_free (chooser_entry->current_folder_path);
-  g_free (chooser_entry->file_part);
+    {
+      g_object_unref (chooser_entry->file_system);
+      chooser_entry->file_system = NULL;
+    }
 
-  parent_class->finalize (object);
+  parent_class->dispose (object);
 }
 
 /* Match functions for the GtkEntryCompletion */
@@ -601,6 +625,41 @@ files_deleted_cb (GtkFileSystem       *file_system,
   /* FIXME: gravy... */
 }
 
+static void
+load_directory_get_folder_callback (GtkFileSystemHandle *handle,
+                                   GtkFileFolder       *folder,
+                                   const GError        *error,
+                                   gpointer             data)
+{
+  gboolean cancelled = handle->cancelled;
+  GtkFileChooserEntry *chooser_entry = data;
+
+  if (handle != chooser_entry->load_folder_handle)
+    goto out;
+
+  chooser_entry->load_folder_handle = NULL;
+
+  if (cancelled || error)
+    goto out;
+
+  chooser_entry->current_folder = folder;
+  g_signal_connect (chooser_entry->current_folder, "files-added",
+                   G_CALLBACK (files_added_cb), chooser_entry);
+  g_signal_connect (chooser_entry->current_folder, "files-removed",
+                   G_CALLBACK (files_deleted_cb), chooser_entry);
+  
+  chooser_entry->completion_store = gtk_list_store_new (N_COLUMNS,
+                                                       G_TYPE_STRING,
+                                                       GTK_TYPE_FILE_PATH);
+
+  gtk_entry_completion_set_model (gtk_entry_get_completion (GTK_ENTRY (chooser_entry)),
+                                 GTK_TREE_MODEL (chooser_entry->completion_store));
+
+out:
+  g_object_unref (chooser_entry);
+  g_object_unref (handle);
+}
+
 static gboolean
 load_directory_callback (GtkFileChooserEntry *chooser_entry)
 {
@@ -623,38 +682,15 @@ load_directory_callback (GtkFileChooserEntry *chooser_entry)
   g_assert (chooser_entry->completion_store == NULL);
 
   /* Load the folder */
-  chooser_entry->current_folder = gtk_file_system_get_folder (chooser_entry->file_system,
-                                                             chooser_entry->current_folder_path,
-                                                             GTK_FILE_INFO_DISPLAY_NAME | GTK_FILE_INFO_IS_FOLDER,
-                                                             NULL); /* NULL-GError */
-
-  /* There is no folder by that name */
-  if (!chooser_entry->current_folder)
-    goto done;
-  g_signal_connect (chooser_entry->current_folder, "files-added",
-                   G_CALLBACK (files_added_cb), chooser_entry);
-  g_signal_connect (chooser_entry->current_folder, "files-removed",
-                   G_CALLBACK (files_deleted_cb), chooser_entry);
-  
-  chooser_entry->completion_store = gtk_list_store_new (N_COLUMNS,
-                                                       G_TYPE_STRING,
-                                                       GTK_TYPE_FILE_PATH);
+  if (chooser_entry->load_folder_handle)
+    gtk_file_system_cancel_operation (chooser_entry->load_folder_handle);
 
-  if (chooser_entry->file_part_pos != -1)
-    {
-      gtk_file_folder_list_children (chooser_entry->current_folder,
-                                    &child_paths,
-                                    NULL); /* NULL-GError */
-      if (child_paths)
-       {
-         update_current_folder_files (chooser_entry, child_paths);
-         add_completion_idle (chooser_entry);
-         gtk_file_paths_free (child_paths);
-       }
-    }
-
-  gtk_entry_completion_set_model (gtk_entry_get_completion (GTK_ENTRY (chooser_entry)),
-                                 GTK_TREE_MODEL (chooser_entry->completion_store));
+  chooser_entry->load_folder_handle =
+    gtk_file_system_get_folder (chooser_entry->file_system,
+                               chooser_entry->current_folder_path,
+                               GTK_FILE_INFO_DISPLAY_NAME | GTK_FILE_INFO_IS_FOLDER,
+                               load_directory_get_folder_callback,
+                               g_object_ref (chooser_entry));
 
  done:
   
@@ -1041,5 +1077,27 @@ _gtk_file_chooser_entry_get_action (GtkFileChooserEntry *chooser_entry)
   return chooser_entry->action;
 }
 
+gboolean
+_gtk_file_chooser_entry_get_is_folder (GtkFileChooserEntry *chooser_entry,
+                                      const GtkFilePath   *path)
+{
+  gboolean retval = FALSE;
+
+  if (chooser_entry->current_folder)
+    {
+      GtkFileInfo *file_info;
+
+      file_info = gtk_file_folder_get_info (chooser_entry->current_folder,
+                                           path, NULL);
+      if (file_info)
+        {
+         retval = gtk_file_info_get_is_folder (file_info);
+         gtk_file_info_free (file_info);
+       }
+    }
+
+  return retval;
+}
+
 #define __GTK_FILE_CHOOSER_ENTRY_C__
 #include "gtkaliasdef.c"
index 04c70dd4d016d3c73c1f06cb521b3cfd56ae06fb..19d06b87c9cc93f3b0645dd5be636894043ae135 100644 (file)
@@ -46,6 +46,8 @@ void               _gtk_file_chooser_entry_set_file_part      (GtkFileChooserEnt
                                                               const gchar         *file_part);
 const GtkFilePath *_gtk_file_chooser_entry_get_current_folder (GtkFileChooserEntry *chooser_entry);
 const gchar *      _gtk_file_chooser_entry_get_file_part      (GtkFileChooserEntry *chooser_entry);
+gboolean           _gtk_file_chooser_entry_get_is_folder      (GtkFileChooserEntry *chooser_entry,
+                                                              const GtkFilePath   *path);
 
 G_END_DECLS
 
index e9d45aae84eb6123567cf47f1bcff141711b5f2d..34ad17b40b0dd42bdd1c5ada3c691b7b82040e61 100644 (file)
@@ -113,6 +113,7 @@ struct _GtkFileChooserDialogPrivate
   gint default_height;
   gboolean resize_horizontally;
   gboolean resize_vertically;
+  gboolean response_requested;
 };
 
 
@@ -187,6 +188,17 @@ struct _GtkFileChooserDefault
 
   GtkTreeModelSort *sort_model;
 
+  /* Handles */
+  GSList *loading_shortcuts;
+  GSList *reload_icon_handles;
+  GtkFileSystemHandle *file_list_drag_data_received_handle;
+  GtkFileSystemHandle *update_current_folder_handle;
+  GtkFileSystemHandle *show_and_select_paths_handle;
+  GtkFileSystemHandle *should_respond_get_info_handle;
+  GtkFileSystemHandle *update_from_entry_handle;
+  GtkFileSystemHandle *shortcuts_activate_iter_handle;
+  GSList *pending_handles;
+
   LoadState load_state;
   ReloadState reload_state;
   guint load_timeout_id;
@@ -267,9 +279,10 @@ struct _GtkFileSystemModel
 
   GSList *idle_clears;
   GSource *idle_clear_source;
-  GSource *idle_finished_loading_source;
 
   gushort max_depth;
+
+  GSList *pending_handles;
   
   guint show_hidden : 1;
   guint show_folders : 1;
@@ -300,6 +313,7 @@ struct _FileModelNode
   guint is_visible : 1;
   guint loaded : 1;
   guint idle_clear : 1;
+  guint load_pending : 1;
 };
 
 
index 4ccc1cac15577e45a7186fc2671f9f8b28f70373..d1af8fe932a6d35122d24924279a4c5ec18e5abd 100644 (file)
@@ -25,6 +25,7 @@
 #include "gtkmodules.h"
 #include "gtkintl.h"
 #include "gtkalias.h"
+#include "gtkstock.h"
 
 #include <string.h>
 
@@ -35,6 +36,7 @@ struct _GtkFileInfo
   gchar *display_name;
   gchar *display_key;
   gchar *mime_type;
+  gchar *icon_name;
   guint is_folder : 1;
   guint is_hidden : 1;
 };
@@ -88,6 +90,10 @@ gtk_file_info_copy (GtkFileInfo *info)
     new_info->display_key = g_strdup (new_info->display_key);
   if (new_info->mime_type)
     new_info->mime_type = g_strdup (new_info->mime_type);
+  if (new_info->icon_name)
+    new_info->icon_name = g_strdup (new_info->icon_name);
+  if (new_info->display_key)
+    new_info->display_key = g_strdup (new_info->display_key);
 
   return new_info;
 }
@@ -103,6 +109,8 @@ gtk_file_info_free (GtkFileInfo *info)
     g_free (info->mime_type);
   if (info->display_key)
     g_free (info->display_key);
+  if (info->icon_name)
+    g_free (info->icon_name);
 
   g_free (info);
 }
@@ -250,6 +258,171 @@ gtk_file_info_set_size (GtkFileInfo *info,
   info->size = size;
 }
 
+void
+gtk_file_info_set_icon_name (GtkFileInfo *info,
+                            const gchar *icon_name)
+{
+  g_return_if_fail (info != NULL);
+  
+  if (info->icon_name)
+    g_free (info->icon_name);
+
+  info->icon_name = g_strdup (icon_name);
+}
+
+G_CONST_RETURN gchar *
+gtk_file_info_get_icon_name (const GtkFileInfo *info)
+{
+  g_return_val_if_fail (info != NULL, NULL);
+  
+  return info->icon_name;
+}
+
+GdkPixbuf *
+gtk_file_info_render_icon (const GtkFileInfo  *info,
+                          GtkWidget          *widget,
+                          gint                pixel_size,
+                          GError            **error)
+{
+  GdkPixbuf *pixbuf = NULL;
+
+  g_return_val_if_fail (info != NULL, NULL);
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+  if (info->icon_name)
+    {
+      if (g_path_is_absolute (info->icon_name))
+       pixbuf = gdk_pixbuf_new_from_file_at_size (info->icon_name,
+                                                  pixel_size,
+                                                  pixel_size,
+                                                  NULL);
+      else
+        {
+          GtkIconTheme *icon_theme;
+
+         icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
+         pixbuf = gtk_icon_theme_load_icon (icon_theme, info->icon_name,
+                                            pixel_size, 0, NULL);
+       }
+    }
+
+  if (!pixbuf)
+    {
+      /* load a fallback icon */
+      pixbuf = gtk_widget_render_icon (widget, GTK_STOCK_FILE, GTK_ICON_SIZE_SMALL_TOOLBAR, NULL);
+      if (!pixbuf && error)
+        g_set_error (error,
+                    GTK_FILE_SYSTEM_ERROR,
+                    GTK_FILE_SYSTEM_ERROR_FAILED,
+                    _("Could not get a stock icon for %s\n"),
+                    info->icon_name);
+    }
+
+  return pixbuf;
+}
+
+/*****************************************
+ *          GtkFileSystemHandle          *
+ *****************************************/
+
+static void gtk_file_system_handle_init       (GtkFileSystemHandle      *handle);
+static void gtk_file_system_handle_class_init (GtkFileSystemHandleClass *klass);
+
+enum
+{
+  PROP_0,
+  PROP_CANCELLED
+};
+
+GType
+gtk_file_system_handle_get_type (void)
+{
+  static GType file_system_handle_type = 0;
+
+  if (!file_system_handle_type)
+    {
+      static const GTypeInfo file_system_handle_info =
+      {
+       sizeof (GtkFileSystemHandleClass),
+       NULL,
+       NULL,
+       (GClassInitFunc) gtk_file_system_handle_class_init,
+       NULL,
+       NULL,
+       sizeof (GtkFileSystemHandle),
+       0,
+       (GInstanceInitFunc) gtk_file_system_handle_init,
+      };
+
+      file_system_handle_type = g_type_register_static (G_TYPE_OBJECT,
+                                                       I_("GtkFileSystemHandle"),
+                                                       &file_system_handle_info, 0);
+    }
+
+  return file_system_handle_type;
+}
+
+#if 0
+GtkFileSystemHandle *
+gtk_file_system_handle_new (void)
+{
+  return g_object_new (GTK_TYPE_FILE_SYSTEM_HANDLE, NULL);
+}
+#endif
+
+static void
+gtk_file_system_handle_init (GtkFileSystemHandle *handle)
+{
+  handle->file_system = NULL;
+  handle->cancelled = FALSE;
+}
+
+static void
+gtk_file_system_handle_set_property (GObject      *object,
+                                    guint         prop_id,
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{
+  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
+
+static void
+gtk_file_system_handle_get_property (GObject    *object,
+                                    guint       prop_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+  GtkFileSystemHandle *handle = GTK_FILE_SYSTEM_HANDLE (object);
+
+  switch (prop_id)
+    {
+      case PROP_CANCELLED:
+       g_value_set_boolean (value, handle->cancelled);
+       break;
+
+      default:
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       break;
+    }
+}
+
+static void
+gtk_file_system_handle_class_init (GtkFileSystemHandleClass *klass)
+{
+  GObjectClass *o_class;
+
+  o_class = (GObjectClass *)klass;
+  o_class->set_property = gtk_file_system_handle_set_property;
+  o_class->get_property = gtk_file_system_handle_get_property;
+
+  g_object_class_install_property (o_class,
+                                  PROP_CANCELLED,
+                                  g_param_spec_boolean ("cancelled",
+                                                        P_("Cancelled"),
+                                                        P_("Whether or not the operation has been successfully cancelled"),
+                                                        FALSE,
+                                                        G_PARAM_READABLE));
+}
 
 /*****************************************
  *             GtkFileSystem             *
@@ -314,29 +487,53 @@ gtk_file_system_list_volumes (GtkFileSystem  *file_system)
   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->list_volumes (file_system);
 }
 
-GtkFileFolder *
-gtk_file_system_get_folder (GtkFileSystem     *file_system,
-                           const GtkFilePath *path,
-                           GtkFileInfoType    types,
-                           GError           **error)
+GtkFileSystemHandle *
+gtk_file_system_get_folder (GtkFileSystem                  *file_system,
+                           const GtkFilePath              *path,
+                           GtkFileInfoType                 types,
+                           GtkFileSystemGetFolderCallback  callback,
+                           gpointer                        data)
 {
   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
   g_return_val_if_fail (path != NULL, NULL);
-  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+  g_return_val_if_fail (callback != NULL, NULL);
 
-  return GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_folder (file_system, path, types, error);
+  return GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_folder (file_system, path, types, callback, data);
 }
 
-gboolean
-gtk_file_system_create_folder(GtkFileSystem     *file_system,
-                             const GtkFilePath *path,
-                             GError           **error)
+GtkFileSystemHandle *
+gtk_file_system_get_info (GtkFileSystem *file_system,
+                         const GtkFilePath *path,
+                         GtkFileInfoType types,
+                         GtkFileSystemGetInfoCallback callback,
+                         gpointer data)
 {
-  g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
-  g_return_val_if_fail (path != NULL, FALSE);
-  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
+  g_return_val_if_fail (path != NULL, NULL);
+  g_return_val_if_fail (callback != NULL, NULL);
+
+  return GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_info (file_system, path, types, callback, data);
+}
+
+GtkFileSystemHandle *
+gtk_file_system_create_folder (GtkFileSystem                     *file_system,
+                              const GtkFilePath                 *path,
+                              GtkFileSystemCreateFolderCallback  callback,
+                              gpointer                           data)
+{
+  g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
+  g_return_val_if_fail (path != NULL, NULL);
+  g_return_val_if_fail (callback != NULL, NULL);
 
-  return GTK_FILE_SYSTEM_GET_IFACE (file_system)->create_folder (file_system, path, error);
+  return GTK_FILE_SYSTEM_GET_IFACE (file_system)->create_folder (file_system, path, callback, data);
+}
+
+void
+gtk_file_system_cancel_operation (GtkFileSystemHandle *handle)
+{
+  g_return_if_fail (GTK_IS_FILE_SYSTEM_HANDLE (handle));
+
+  return GTK_FILE_SYSTEM_GET_IFACE (handle->file_system)->cancel_operation (handle);
 }
 
 /**
@@ -432,16 +629,18 @@ gtk_file_system_volume_get_is_mounted (GtkFileSystem       *file_system,
  * 
  * Return value: TRUE if the @volume was mounted successfully, FALSE otherwise.
  **/
-gboolean
-gtk_file_system_volume_mount (GtkFileSystem        *file_system, 
-                             GtkFileSystemVolume  *volume,
-                             GError              **error)
+/* FIXME XXX: update documentation above */
+GtkFileSystemHandle *
+gtk_file_system_volume_mount (GtkFileSystem                    *file_system,
+                             GtkFileSystemVolume              *volume,
+                             GtkFileSystemVolumeMountCallback  callback,
+                             gpointer                          data)
 {
-  g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
-  g_return_val_if_fail (volume != NULL, FALSE);
-  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
+  g_return_val_if_fail (volume != NULL, NULL);
+  g_return_val_if_fail (callback != NULL, NULL);
 
-  return GTK_FILE_SYSTEM_GET_IFACE (file_system)->volume_mount (file_system, volume, error);
+  return GTK_FILE_SYSTEM_GET_IFACE (file_system)->volume_mount (file_system, volume, callback, data);
 }
 
 /**
@@ -485,17 +684,53 @@ gtk_file_system_volume_render_icon (GtkFileSystem        *file_system,
                                    gint                  pixel_size,
                                    GError              **error)
 {
+  gchar *icon_name;
+  GdkPixbuf *pixbuf;
+
   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
   g_return_val_if_fail (volume != NULL, NULL);
   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
   g_return_val_if_fail (pixel_size > 0, NULL);
   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-  return GTK_FILE_SYSTEM_GET_IFACE (file_system)->volume_render_icon (file_system,
-                                                                     volume,
-                                                                     widget,
-                                                                     pixel_size,
-                                                                     error);
+  icon_name = gtk_file_system_volume_get_icon_name (file_system, volume,
+                                                   error);
+  if (!icon_name)
+    {
+      return NULL;
+    }
+
+  pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget)),
+                                    icon_name, pixel_size, 0, NULL);
+  g_free (icon_name);
+
+  return pixbuf;
+}
+
+/**
+ * gtk_file_system_volume_get_icon_name:
+ * @file_system: a #GtkFileSystem
+ * @volume: a #GtkFileSystemVolume
+ * @error: location to store error, or %NULL
+ * 
+ * Gets an icon name suitable for a #GtkFileSystemVolume.
+ * 
+ * Return value: An icon name which can be used for rendering an icon for
+ * this volume, or %NULL if no icon name could be found.  In the latter
+ * case, the @error value will be set as appropriate.
+ **/
+gchar *
+gtk_file_system_volume_get_icon_name (GtkFileSystem        *file_system,
+                                     GtkFileSystemVolume  *volume,
+                                     GError              **error)
+{
+  g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
+  g_return_val_if_fail (volume != NULL, NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+  return GTK_FILE_SYSTEM_GET_IFACE (file_system)->volume_get_icon_name (file_system,
+                                                                       volume,
+                                                                       error);
 }
 
 /**
@@ -682,21 +917,6 @@ gtk_file_system_path_is_local (GtkFileSystem     *file_system,
   return result;
 }
 
-GdkPixbuf *
-gtk_file_system_render_icon (GtkFileSystem      *file_system,
-                            const GtkFilePath  *path,
-                            GtkWidget          *widget,
-                            gint                pixel_size,
-                            GError            **error)
-{
-  g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
-  g_return_val_if_fail (path != NULL, NULL);
-  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
-  g_return_val_if_fail (pixel_size > 0, NULL);
-
-  return GTK_FILE_SYSTEM_GET_IFACE (file_system)->render_icon (file_system, path, widget, pixel_size, error);
-}
-
 /**
  * gtk_file_system_insert_bookmark:
  * @file_system: a #GtkFileSystem
index 6f038fdbc6ea64a73d30bab248c81232a8391972..57c458a028970bb8b9a8f84bf0226b881157ddac 100644 (file)
@@ -55,7 +55,8 @@ typedef enum {
   GTK_FILE_INFO_MIME_TYPE         = 1 << 3,
   GTK_FILE_INFO_MODIFICATION_TIME = 1 << 4,
   GTK_FILE_INFO_SIZE              = 1 << 5,
-  GTK_FILE_INFO_ALL               = (1 << 6) - 1
+  GTK_FILE_INFO_ICON              = 1 << 6,
+  GTK_FILE_INFO_ALL               = (1 << 7) - 1
 } GtkFileInfoType;
 
 /* GError enumeration for GtkFileSystem
@@ -106,6 +107,43 @@ gint64                gtk_file_info_get_size              (const GtkFileInfo *in
 void                  gtk_file_info_set_size              (GtkFileInfo       *info,
                                                           gint64             size);
 
+void                  gtk_file_info_set_icon_name         (GtkFileInfo       *info,
+                                                          const gchar       *con_name);
+G_CONST_RETURN gchar *gtk_file_info_get_icon_name         (const GtkFileInfo *info);
+GdkPixbuf            *gtk_file_info_render_icon           (const GtkFileInfo *info,
+                                                          GtkWidget         *widget,
+                                                          gint               pixel_size,
+                                                          GError           **error);
+
+/* GtkFileSystemHandle
+ */
+
+#define GTK_TYPE_FILE_SYSTEM_HANDLE            (gtk_file_system_handle_get_type ())
+#define GTK_FILE_SYSTEM_HANDLE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_SYSTEM_HANDLE, GtkFileSystemHandle))
+#define GTK_IS_FILE_SYSTEM_HANDLE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_SYSTEM_HANDLE))
+#define GTK_FILE_SYSTEM_HANDLE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_HANDLE, GtkFileSystemHandleUnixClass))
+#define GTK_IS_FILE_SYSTEM_HANDLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_SYSTEM_HANDLE))
+#define GTK_FILE_SYSTEM_HANDLE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_SYSTEM_HANDLE, GtkFileSystemHandleClass))
+
+typedef struct _GtkFileSystemHandle       GtkFileSystemHandle;
+typedef struct _GtkFileSystemHandleClass  GtkFileSystemHandleClass;
+
+struct _GtkFileSystemHandle
+{
+  GObject parent_instance;
+
+  GtkFileSystem *file_system;
+
+  guint cancelled : 1;
+};
+
+struct _GtkFileSystemHandleClass
+{
+  GObjectClass parent_class;
+};
+
+GType gtk_file_system_handle_get_type (void);
+
 /* The base GtkFileSystem interface
  */
 #define GTK_TYPE_FILE_SYSTEM             (gtk_file_system_get_type ())
@@ -113,6 +151,29 @@ void                  gtk_file_info_set_size              (GtkFileInfo       *in
 #define GTK_IS_FILE_SYSTEM(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_SYSTEM))
 #define GTK_FILE_SYSTEM_GET_IFACE(inst)  (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GTK_TYPE_FILE_SYSTEM, GtkFileSystemIface))
 
+/* Callbacks for the asynchronous GtkFileSystem operations
+ */
+
+typedef void (* GtkFileSystemGetInfoCallback) (GtkFileSystemHandle *handle,
+                                              const GtkFileInfo   *file_info,
+                                              const GError        *error,
+                                              gpointer             data);
+typedef void (* GtkFileSystemGetFolderCallback) (GtkFileSystemHandle *handle,
+                                                GtkFileFolder       *folder,
+                                                const GError        *error,
+                                                gpointer             data);
+typedef void (* GtkFileSystemCreateFolderCallback) (GtkFileSystemHandle *handle,
+                                                   const GtkFilePath   *path,
+                                                   const GError        *error,
+                                                   gpointer             data);
+typedef void (* GtkFileSystemVolumeMountCallback) (GtkFileSystemHandle *handle,
+                                                  GtkFileSystemVolume *volume,
+                                                  const GError        *error,
+                                                  gpointer             data);
+
+/*
+ */
+
 struct _GtkFileSystemIface
 {
   GTypeInterface base_iface;
@@ -123,13 +184,22 @@ struct _GtkFileSystemIface
   GtkFileSystemVolume * (*get_volume_for_path) (GtkFileSystem     *file_system,
                                                const GtkFilePath *path);
 
-  GtkFileFolder *    (*get_folder)     (GtkFileSystem     *file_system,
-                                       const GtkFilePath *path,
-                                       GtkFileInfoType    types,
-                                       GError           **error);
-  gboolean           (*create_folder)  (GtkFileSystem     *file_system,
-                                       const GtkFilePath *path,
-                                       GError           **error);
+  GtkFileSystemHandle * (*get_folder)  (GtkFileSystem                  *file_system,
+                                       const GtkFilePath              *path,
+                                       GtkFileInfoType                 types,
+                                       GtkFileSystemGetFolderCallback  callback,
+                                       gpointer                        data);
+  GtkFileSystemHandle * (*get_info) (GtkFileSystem                *file_system,
+                                    const GtkFilePath            *path,
+                                    GtkFileInfoType               types,
+                                    GtkFileSystemGetInfoCallback  callback,
+                                    gpointer                      data);
+  GtkFileSystemHandle * (*create_folder)  (GtkFileSystem                     *file_system,
+                                          const GtkFilePath                 *path,
+                                          GtkFileSystemCreateFolderCallback  callback,
+                                          gpointer                           data);
+
+  void               (*cancel_operation) (GtkFileSystemHandle *handle);
 
   /* Volumes
    */
@@ -139,15 +209,14 @@ struct _GtkFileSystemIface
                                            GtkFileSystemVolume  *volume);
   gboolean      (*volume_get_is_mounted)   (GtkFileSystem        *file_system,
                                            GtkFileSystemVolume  *volume);
-  gboolean      (*volume_mount)            (GtkFileSystem        *file_system, 
-                                           GtkFileSystemVolume  *volume,
-                                           GError              **error);
-  char *        (*volume_get_display_name) (GtkFileSystem        *file_system, 
+  GtkFileSystemHandle * (*volume_mount)    (GtkFileSystem                    *file_system,
+                                           GtkFileSystemVolume              *volume,
+                                           GtkFileSystemVolumeMountCallback  callback,
+                                           gpointer                          data);
+  char *        (*volume_get_display_name) (GtkFileSystem        *file_system,
                                            GtkFileSystemVolume  *volume);
-  GdkPixbuf *   (*volume_render_icon)      (GtkFileSystem        *file_system,
+  gchar *       (*volume_get_icon_name)    (GtkFileSystem        *file_system,
                                            GtkFileSystemVolume  *volume,
-                                           GtkWidget            *widget,
-                                           gint                  pixel_size,
                                            GError              **error);
 
   /* Path Manipulation
@@ -175,14 +244,6 @@ struct _GtkFileSystemIface
   GtkFilePath *(*filename_to_path) (GtkFileSystem      *file_system,
                                    const gchar        *path);
 
-  /* Icons 
-   */
-  GdkPixbuf *  (*render_icon)    (GtkFileSystem     *file_system,
-                                 const GtkFilePath *path,
-                                 GtkWidget         *widget,
-                                 gint               pixel_size,
-                                 GError           **error);
-
   /* Bookmarks 
    */
   gboolean       (*insert_bookmark)        (GtkFileSystem     *file_system,
@@ -221,9 +282,10 @@ GtkFilePath *     gtk_file_system_volume_get_base_path    (GtkFileSystem
                                                           GtkFileSystemVolume  *volume);
 gboolean          gtk_file_system_volume_get_is_mounted   (GtkFileSystem        *file_system,
                                                           GtkFileSystemVolume  *volume);
-gboolean          gtk_file_system_volume_mount            (GtkFileSystem        *file_system, 
-                                                          GtkFileSystemVolume  *volume,
-                                                          GError              **error);
+GtkFileSystemHandle *gtk_file_system_volume_mount         (GtkFileSystem                    *file_system,
+                                                          GtkFileSystemVolume              *volume,
+                                                          GtkFileSystemVolumeMountCallback  callback,
+                                                          gpointer                          data);
 char *            gtk_file_system_volume_get_display_name (GtkFileSystem        *file_system, 
                                                           GtkFileSystemVolume  *volume);
 GdkPixbuf *       gtk_file_system_volume_render_icon      (GtkFileSystem        *file_system,
@@ -231,18 +293,29 @@ GdkPixbuf *       gtk_file_system_volume_render_icon      (GtkFileSystem
                                                           GtkWidget            *widget,
                                                           gint                  pixel_size,
                                                           GError              **error);
+gchar *           gtk_file_system_volume_get_icon_name    (GtkFileSystem        *file_system,
+                                                          GtkFileSystemVolume  *volume,
+                                                          GError              **error);
 
 gboolean          gtk_file_system_get_parent     (GtkFileSystem     *file_system,
                                                  const GtkFilePath *path,
                                                  GtkFilePath      **parent,
                                                  GError           **error);
-GtkFileFolder    *gtk_file_system_get_folder     (GtkFileSystem     *file_system,
-                                                 const GtkFilePath *path,
-                                                 GtkFileInfoType    types,
-                                                 GError           **error);
-gboolean          gtk_file_system_create_folder  (GtkFileSystem     *file_system,
-                                                 const GtkFilePath *path,
-                                                 GError           **error);
+GtkFileSystemHandle *gtk_file_system_get_folder  (GtkFileSystem                  *file_system,
+                                                 const GtkFilePath              *path,
+                                                 GtkFileInfoType                 types,
+                                                 GtkFileSystemGetFolderCallback  callback,
+                                                 gpointer                        data);
+GtkFileSystemHandle *gtk_file_system_get_info    (GtkFileSystem                  *file_system,
+                                                 const GtkFilePath              *path,
+                                                 GtkFileInfoType                 types,
+                                                 GtkFileSystemGetInfoCallback    callback,
+                                                 gpointer                        data);
+GtkFileSystemHandle *gtk_file_system_create_folder (GtkFileSystem                     *file_system,
+                                                   const GtkFilePath                 *path,
+                                                   GtkFileSystemCreateFolderCallback  callback,
+                                                   gpointer                           data);
+void              gtk_file_system_cancel_operation (GtkFileSystemHandle *handle);
 GtkFilePath *     gtk_file_system_make_path      (GtkFileSystem     *file_system,
                                                  const GtkFilePath *base_path,
                                                  const gchar       *display_name,
@@ -266,12 +339,6 @@ GtkFilePath *gtk_file_system_filename_to_path (GtkFileSystem     *file_system,
 gboolean     gtk_file_system_path_is_local    (GtkFileSystem     *filesystem,
                                               const GtkFilePath *path);
 
-GdkPixbuf   *gtk_file_system_render_icon   (GtkFileSystem      *file_system,
-                                           const GtkFilePath  *path,
-                                           GtkWidget          *widget,
-                                           gint                pixel_size,
-                                           GError            **error);
-
 gboolean gtk_file_system_insert_bookmark (GtkFileSystem     *file_system,
                                          const GtkFilePath *path,
                                          gint               position,
index b2ad218ea9a688e3cdfa26991b789e7bda91fa4b..d365351add5a77cca349f91d082238eb143c62fd 100644 (file)
@@ -50,6 +50,7 @@ static void gtk_file_system_model_class_init   (GtkFileSystemModelClass *class);
 static void gtk_file_system_model_iface_init   (GtkTreeModelIface       *iface);
 static void gtk_file_system_model_init         (GtkFileSystemModel      *model);
 static void gtk_file_system_model_finalize     (GObject                 *object);
+static void gtk_file_system_model_dispose      (GObject                 *object);
 
 static void drag_source_iface_init (GtkTreeDragSourceIface *iface);
 
@@ -205,6 +206,7 @@ gtk_file_system_model_class_init (GtkFileSystemModelClass *class)
   parent_class = g_type_class_peek_parent (class);
 
   gobject_class->finalize = gtk_file_system_model_finalize;
+  gobject_class->dispose = gtk_file_system_model_dispose;
 
   file_system_model_signals[FINISHED_LOADING] =
     g_signal_new (I_("finished-loading"),
@@ -258,9 +260,6 @@ gtk_file_system_model_finalize (GObject *object)
   if (model->file_system)
     g_object_unref (model->file_system);
 
-  if (model->idle_finished_loading_source)
-    g_source_destroy (model->idle_finished_loading_source);
-
   children = model->roots;
   while (children)
     {
@@ -272,6 +271,25 @@ gtk_file_system_model_finalize (GObject *object)
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
+
+static void
+gtk_file_system_model_dispose (GObject *object)
+{
+  GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object);
+
+  if (model->pending_handles)
+    {
+      GSList *l;
+
+      for (l = model->pending_handles; l; l = l->next)
+        gtk_file_system_cancel_operation (l->data);
+      g_slist_free (model->pending_handles);
+      model->pending_handles = NULL;
+    }
+
+  G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
 static void
 drag_source_iface_init (GtkTreeDragSourceIface *iface)
 {
@@ -630,33 +648,75 @@ root_folder_finished_loading_cb (GtkFileFolder      *folder,
   g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0);
 }
 
-/* Emits the "finished-loading" signal as an idle handler; see the comment in
- * _gtk_file_system_model_new()
- */
-static gboolean
-idle_finished_loading_cb (GtkFileSystemModel *model)
+static void
+got_root_folder_cb (GtkFileSystemHandle *handle,
+                   GtkFileFolder       *folder,
+                   const GError        *error,
+                   gpointer             data)
 {
-  GDK_THREADS_ENTER ();
+  GSList *roots = NULL;
+  GSList *tmp_list;
+  gboolean cancelled = handle->cancelled;
+  GtkFileSystemModel *model = data;
 
-  g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0);
+  tmp_list = g_slist_find (model->pending_handles, handle);
+  if (!tmp_list)
+    goto out;
 
-  g_source_destroy (model->idle_finished_loading_source);
-  model->idle_finished_loading_source = NULL;
+  model->pending_handles = g_slist_remove_link (model->pending_handles,
+                                               tmp_list);
 
-  GDK_THREADS_LEAVE ();
+  if (cancelled || !folder)
+    goto out;
 
-  return FALSE;
-}
+  model->root_folder = folder;
 
-/* Queues an idle handler to emit the "finished-loading" signal */
-static void
-queue_finished_loading (GtkFileSystemModel *model)
-{
-  model->idle_finished_loading_source = g_idle_source_new ();
-  g_source_set_closure (model->idle_finished_loading_source,
-                       g_cclosure_new_object (G_CALLBACK (idle_finished_loading_cb),
-                                              G_OBJECT (model)));
-  g_source_attach (model->idle_finished_loading_source, NULL);
+  if (gtk_file_folder_is_finished_loading (model->root_folder))
+    g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0);
+  else
+    g_signal_connect_object (model->root_folder, "finished-loading",
+                            G_CALLBACK (root_folder_finished_loading_cb), model, 0);
+
+  gtk_file_folder_list_children (model->root_folder, &roots, NULL);
+
+  g_signal_connect_object (model->root_folder, "deleted",
+                          G_CALLBACK (root_deleted_callback), model, 0);
+  g_signal_connect_object (model->root_folder, "files-added",
+                          G_CALLBACK (root_files_added_callback), model, 0);
+  g_signal_connect_object (model->root_folder, "files-changed",
+                          G_CALLBACK (root_files_changed_callback), model, 0);
+  g_signal_connect_object (model->root_folder, "files-removed",
+                          G_CALLBACK (root_files_removed_callback), model, 0);
+
+  roots = gtk_file_paths_sort (roots);
+  
+  for (tmp_list = roots; tmp_list; tmp_list = tmp_list->next)
+    {
+      FileModelNode *node = file_model_node_new (model, tmp_list->data);
+      gtk_file_path_free (tmp_list->data);
+      node->is_visible = file_model_node_is_visible (model, node);
+      node->next = model->roots;
+      node->depth = 0;
+      model->roots = node;
+
+      if (node->is_visible)
+        {
+         GtkTreeIter iter;
+         GtkTreePath *path;
+
+         iter.user_data = node;
+         path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
+         gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
+         gtk_tree_path_free (path);
+       }
+    }
+  g_slist_free (roots);
+
+  model->roots = (FileModelNode *) g_slist_reverse ((GSList *)model->roots);
+
+out:
+  g_object_unref (model);
+  g_object_unref (handle);
 }
 
 /**
@@ -691,28 +751,15 @@ _gtk_file_system_model_new (GtkFileSystem     *file_system,
                            GError           **error)
 {
   GtkFileSystemModel *model;
-  GtkFileFolder *root_folder;
-  GSList *roots;
-  GSList *tmp_list;
+  GtkFileSystemHandle *handle;
 
   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
   g_return_val_if_fail (root_path != NULL, NULL);
   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-  /* First, try to load the folder */
+  /* First, start loading the root folder */
 
   types |= GTK_FILE_INFO_IS_FOLDER | GTK_FILE_INFO_IS_HIDDEN;
-  
-  root_folder = gtk_file_system_get_folder (file_system, root_path, types, error);
-
-  if (!root_folder)
-    return NULL;
-
-  if (!gtk_file_folder_list_children (root_folder, &roots, error))
-    {
-      g_object_unref (root_folder);
-      return NULL;
-    }
 
   /* Then, actually create the model and the root nodes */
 
@@ -724,39 +771,32 @@ _gtk_file_system_model_new (GtkFileSystem     *file_system,
     model->max_depth = MIN (max_depth, G_MAXUSHORT);
 
   model->types = types;
-  model->root_folder = root_folder;
+  model->root_folder = NULL;
   model->root_path = gtk_file_path_copy (root_path);
 
-  if (gtk_file_folder_is_finished_loading (model->root_folder))
-    queue_finished_loading (model); /* done in an idle because we are being created */
-  else
-    g_signal_connect_object (model->root_folder, "finished-loading",
-                            G_CALLBACK (root_folder_finished_loading_cb), model, 0);
-
-  g_signal_connect_object (model->root_folder, "deleted",
-                          G_CALLBACK (root_deleted_callback), model, 0);
-  g_signal_connect_object (model->root_folder, "files-added",
-                          G_CALLBACK (root_files_added_callback), model, 0);
-  g_signal_connect_object (model->root_folder, "files-changed",
-                          G_CALLBACK (root_files_changed_callback), model, 0);
-  g_signal_connect_object (model->root_folder, "files-removed",
-                          G_CALLBACK (root_files_removed_callback), model, 0);
+  model->roots = NULL;
 
-  roots = gtk_file_paths_sort (roots);
-  
-  for (tmp_list = roots; tmp_list; tmp_list = tmp_list->next)
+  handle = gtk_file_system_get_folder (file_system, root_path, types,
+                                      got_root_folder_cb,
+                                      g_object_ref (model));
+  if (!handle)
     {
-      FileModelNode *node = file_model_node_new (model, tmp_list->data);
-      gtk_file_path_free (tmp_list->data);
-      node->is_visible = file_model_node_is_visible (model, node);
-      node->next = model->roots;
-      node->depth = 0;
-      model->roots = node;
+      /* In this case got_root_folder_cb() will never be called, so we
+       * need to unref model twice.
+       */
+      g_object_unref (model);
+      g_object_unref (model);
+
+      g_set_error (error,
+                  GTK_FILE_CHOOSER_ERROR,
+                  GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
+                  _("Could not obtain root folder"));
+
+      return NULL;
     }
-  g_slist_free (roots);
 
-  model->roots = (FileModelNode *) g_slist_reverse ((GSList *)model->roots);
-  
+  model->pending_handles = g_slist_append (model->pending_handles, handle);
+
   return model;
 }
 
@@ -988,58 +1028,6 @@ find_child_node (GtkFileSystemModel *model,
 
   return NULL;
 }
-                
-
-static FileModelNode *
-find_and_ref_path (GtkFileSystemModel  *model,
-                  const GtkFilePath   *path,
-                  GSList             **cleanups)
-{
-  GtkFilePath *parent_path;
-  FileModelNode *parent_node;
-  FileModelNode *child_node;
-  GtkFileFolder *folder;
-
-  if (gtk_file_path_compare (path, model->root_path) == 0
-      || !gtk_file_system_get_parent (model->file_system, path, &parent_path, NULL))
-    return NULL;
-
-  if (parent_path)
-    {
-      parent_node = find_and_ref_path (model, parent_path, cleanups);
-      gtk_file_path_free (parent_path);
-    }
-  else
-    parent_node = NULL;
-
-  child_node = find_child_node (model, parent_node, path);
-  if (child_node)
-    {
-      file_model_node_ref (child_node);
-      return child_node;
-    }
-
-  folder = gtk_file_system_get_folder (model->file_system,
-                                      path,
-                                      model->types,
-                                      NULL);   /* NULL-GError */
-  if (folder)
-    {
-      *cleanups = g_slist_prepend (*cleanups, folder);
-
-      child_node = find_child_node (model, parent_node, path);
-      if (child_node)
-       {
-         file_model_node_ref (child_node);
-         return child_node;
-       }
-    }
-
-  if (parent_node)
-    unref_node_and_parents (model, parent_node);
-
-  return NULL;
-}
 
 /**
  * _gtk_file_system_model_set_filter:
@@ -1064,6 +1052,126 @@ _gtk_file_system_model_set_filter (GtkFileSystemModel      *model,
   model_refilter_all (model);
 }
 
+
+struct RefPathData
+{
+  GtkFileSystemModel *model;
+  FileModelNode *node;
+  FileModelNode *parent_node;
+  GSList *paths;
+  GSList *cleanups;
+  GtkFileSystemModelPathFunc func;
+  gpointer user_data;
+};
+
+/* FIXME: maybe we have to wait on finished-loading? */
+static void
+ref_path_cb (GtkFileSystemHandle *handle,
+            GtkFileFolder       *folder,
+            const GError        *error,
+            gpointer             data)
+{
+  struct RefPathData *info = data;
+  gboolean cancelled = handle->cancelled;
+
+  if (!g_slist_find (info->model->pending_handles, handle))
+    goto out;
+
+  info->model->pending_handles = g_slist_remove (info->model->pending_handles, handle);
+
+  /* Note that !folder means that the child node was already
+   * found, without using get_folder.
+   */
+  if (cancelled || error)
+    goto out;
+
+  if (folder)
+    info->cleanups = g_slist_prepend (info->cleanups, folder);
+  else if (g_slist_length (info->paths) == 1
+           && gtk_file_path_compare (info->node->path, info->paths->data) == 0)
+    {
+      /* Done, now call the function */
+      if (info->node)
+        {
+          GtkTreeIter iter;
+          GtkTreePath *path;
+
+          iter.user_data = info->node;
+          path = gtk_tree_model_get_path (GTK_TREE_MODEL (info->model), &iter);
+
+          (* info->func) (info->model, path, &iter, info->user_data);
+
+          gtk_tree_path_free (path);
+        }
+
+      goto out;
+    }
+
+  info->node = find_child_node (info->model, info->parent_node, info->paths->data);
+  if (info->node)
+    file_model_node_ref (info->node);
+  else
+    {
+      goto out;
+    }
+
+  gtk_file_path_free (info->paths->data);
+  info->paths = g_slist_remove (info->paths, info->paths->data);
+
+  if (g_slist_length (info->paths) < 1)
+    {
+      /* Done, now call the function */
+      if (info->node)
+        {
+          GtkTreeIter iter;
+          GtkTreePath *path;
+
+          iter.user_data = info->node;
+          path = gtk_tree_model_get_path (GTK_TREE_MODEL (info->model), &iter);
+
+          (* info->func) (info->model, path, &iter, info->user_data);
+
+          gtk_tree_path_free (path);
+        }
+
+      goto out;
+    }
+  else
+    {
+      info->parent_node = info->node;
+
+      if (info->parent_node->loaded)
+        {
+          info->node = find_child_node (info->model, info->parent_node, info->paths->data);
+          ref_path_cb (NULL, NULL, NULL, info);
+        }
+      else
+        {
+         GtkFileSystemHandle *handle;
+
+          handle = gtk_file_system_get_folder (info->model->file_system,
+                                              info->paths->data,
+                                              info->model->types,
+                                              ref_path_cb, data);
+         info->model->pending_handles =
+           g_slist_append (info->model->pending_handles, handle);
+        }
+
+      return;
+    }
+
+out:
+  if (info->node)
+    unref_node_and_parents (info->model, info->node);
+  gtk_file_paths_free (info->paths);
+  g_slist_foreach (info->cleanups, (GFunc)g_object_unref, NULL);
+  g_slist_free (info->cleanups);
+  g_object_unref (info->model);
+  g_free (info);
+
+  g_object_unref (handle);
+}
+
 /**
  * _gtk_file_system_model_path_do:
  * @model: a #GtkFileSystemModel
@@ -1090,33 +1198,90 @@ _gtk_file_system_model_set_filter (GtkFileSystemModel      *model,
  * Return value: %TRUE if the path was successfully
  *  found in @model and @func was called.
  **/
-gboolean
-_gtk_file_system_model_path_do (GtkFileSystemModel       *model,
-                              const GtkFilePath         *path,
-                              GtkFileSystemModelPathFunc func,
-                              gpointer                   user_data)
+void
+_gtk_file_system_model_path_do (GtkFileSystemModel        *model,
+                               const GtkFilePath         *path,
+                               GtkFileSystemModelPathFunc func,
+                               gpointer                   user_data)
 {
-  GSList *cleanups = NULL;
-  FileModelNode *node = find_and_ref_path (model, path, &cleanups);
+  GtkFilePath *parent_path;
+  GSList *paths = NULL;
+  FileModelNode *node;
+  struct RefPathData *info;
 
-  if (node)
-    {
-      GtkTreeIter iter;
-      GtkTreePath *path;
+  if (gtk_file_path_compare (path, model->root_path) == 0
+      || !gtk_file_system_get_parent (model->file_system, path, &parent_path, NULL))
+    return;
 
-      iter.user_data = node;
-      path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
+  paths = g_slist_prepend (paths, gtk_file_path_copy (path));
+  while (gtk_file_path_compare (parent_path, model->root_path) != 0)
+    {
+      paths = g_slist_prepend (paths, parent_path);
+      if (!gtk_file_system_get_parent (model->file_system, parent_path, &parent_path, NULL))
+        {
+         gtk_file_paths_free (paths);
+         return;
+       }
+    }
 
-      (*func) (model, path, &iter, user_data);
+  if (g_slist_length (paths) < 1)
+    return;
 
-      gtk_tree_path_free (path);
-      unref_node_and_parents (model, node);
+  /* Now we have all paths, except the root path */
+  node = find_child_node (model, NULL, paths->data);
+  if (!node)
+    {
+      gtk_file_paths_free (paths);
+      return;
     }
 
-  g_slist_foreach (cleanups, (GFunc)g_object_unref, NULL);
-  g_slist_free (cleanups);
+  file_model_node_ref (node);
 
-  return node != NULL;
+  gtk_file_path_free (paths->data);
+  paths = g_slist_remove (paths, paths->data);
+
+  if (g_slist_length (paths) < 1)
+    {
+      /* Done, now call the function */
+      if (node)
+        {
+          GtkTreeIter iter;
+          GtkTreePath *path;
+
+          iter.user_data = node;
+          path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
+
+          (* func) (model, path, &iter, user_data);
+
+          gtk_tree_path_free (path);
+          unref_node_and_parents (model, node);
+        }
+    }
+  else
+    {
+      info = g_new0 (struct RefPathData, 1);
+      info->paths = paths;
+      info->model = g_object_ref (model);
+      info->func = func;
+      info->user_data = user_data;
+      info->node = node;
+
+      if (info->node->loaded)
+        {
+         info->parent_node = info->node;
+          info->node = find_child_node (model, info->parent_node, info->paths->data);
+          ref_path_cb (NULL, NULL, NULL, info);
+        }
+      else
+        {
+         GtkFileSystemHandle *handle;
+
+          handle = gtk_file_system_get_folder (model->file_system,
+                                              paths->data, model->types,
+                                              ref_path_cb, info);
+         model->pending_handles = g_slist_append (model->pending_handles, handle);
+        }
+    }
 }
 
 /**
@@ -1389,6 +1554,114 @@ file_model_node_child_unref (FileModelNode *parent)
     file_model_node_idle_clear (parent);
 }
 
+struct GetChildrenData
+{
+  GtkFileSystemModel *model;
+  FileModelNode *node;
+};
+
+static void
+get_children_get_folder_cb (GtkFileSystemHandle *handle,
+                           GtkFileFolder       *folder,
+                           const GError        *error,
+                           gpointer             callback_data)
+{
+  GSList *child_paths, *tmp_list;
+  gboolean has_children = FALSE;
+  gboolean cancelled = handle->cancelled;
+  struct GetChildrenData *data = callback_data;
+
+  tmp_list = g_slist_find (data->model->pending_handles, handle);
+
+  if (!tmp_list)
+    goto out;
+
+  data->model->pending_handles = g_slist_remove_link (data->model->pending_handles, tmp_list);
+
+  if (cancelled || !folder)
+    {
+      /* error, no folder, remove dummy child */
+      if (data->node->parent && data->node->parent->has_dummy)
+        {
+          data->node->parent->children = NULL;
+          data->node->parent->has_dummy = FALSE;
+       }
+
+      file_model_node_free (data->node);
+
+      goto out;
+    }
+
+  data->node->folder = folder;
+  data->node->load_pending = FALSE;
+
+  if (gtk_file_folder_list_children (folder, &child_paths, NULL)) /* NULL-GError */
+    {
+      child_paths = gtk_file_paths_sort (child_paths);
+
+      for (tmp_list = child_paths; tmp_list; tmp_list = tmp_list->next)
+       {
+         FileModelNode *child_node = file_model_node_new (data->model, tmp_list->data);
+         gtk_file_path_free (tmp_list->data);
+         child_node->next = data->node->children;
+         child_node->parent = data->node;
+         child_node->depth = data->node->depth + 1;
+         child_node->is_visible = file_model_node_is_visible (data->model, child_node);
+
+         if (child_node->is_visible)
+           {
+             GtkTreeIter iter;
+             GtkTreePath *path;
+
+             has_children = TRUE;
+
+             iter.user_data = child_node;
+             path = gtk_tree_model_get_path (GTK_TREE_MODEL (data->model), &iter);
+             gtk_tree_model_row_inserted (GTK_TREE_MODEL (data->model), path, &iter);
+             gtk_tree_path_free (path);
+           }
+
+         data->node->children = child_node;
+       }
+      g_slist_free (child_paths);
+    }
+
+  data->node->children = (FileModelNode *)g_slist_reverse ((GSList *)data->node->children);
+
+  g_signal_connect (data->node->folder, "deleted",
+                   G_CALLBACK (deleted_callback), data->node);
+  g_signal_connect (data->node->folder, "files-added",
+                   G_CALLBACK (files_added_callback), data->node);
+  g_signal_connect (data->node->folder, "files-changed",
+                   G_CALLBACK (files_changed_callback), data->node);
+  g_signal_connect (data->node->folder, "files-removed",
+                   G_CALLBACK (files_removed_callback), data->node);
+
+  data->node->loaded = TRUE;
+
+  if (!has_children)
+    {
+      /* The hard case ... we claimed this folder had children, but actually
+       * it didn't. We have to add a dummy child, possibly to remove later.
+       */
+      FileModelNode *child_node = file_model_node_new (data->model, NULL);
+      child_node->is_visible = TRUE;
+      child_node->parent = data->node;
+      child_node->is_dummy = TRUE;
+
+      data->node->children = child_node;
+      data->node->has_dummy = TRUE;
+    }
+
+  g_object_set_data (G_OBJECT (data->node->folder), I_("model-node"), data->node);
+
+out:
+  g_object_unref (data->model);
+  g_free (data);
+
+  g_object_unref (handle);
+}
+
 static FileModelNode *
 file_model_node_get_children (GtkFileSystemModel *model,
                              FileModelNode      *node)
@@ -1396,7 +1669,7 @@ file_model_node_get_children (GtkFileSystemModel *model,
   if (node->ref_count == 0)
     return NULL;
 
-  if (!node->loaded)
+  if (!node->loaded && !node->load_pending)
     {
       const GtkFileInfo *info = file_model_node_get_info (model, node);
       gboolean has_children = FALSE;
@@ -1405,48 +1678,25 @@ file_model_node_get_children (GtkFileSystemModel *model,
       file_model_node_idle_clear_cancel (node);
 
       if (is_folder)
-       node->folder = gtk_file_system_get_folder (model->file_system,
-                                                  node->path,
-                                                  model->types,
-                                                  NULL);       /* NULL-GError */
-
-      if (node->folder)
-       {
-         GSList *child_paths, *tmp_list;
-         
-         if (gtk_file_folder_list_children (node->folder, &child_paths, NULL)) /* NULL-GError */
-           {
-             child_paths = gtk_file_paths_sort (child_paths);
-
-             for (tmp_list = child_paths; tmp_list; tmp_list = tmp_list->next)
-               {
-                 FileModelNode *child_node = file_model_node_new (model, tmp_list->data);
-                 gtk_file_path_free (tmp_list->data);
-                 child_node->next = node->children;
-                 child_node->parent = node;
-                 child_node->depth = node->depth + 1;
-                 child_node->is_visible = file_model_node_is_visible (model, child_node);
-                 if (child_node->is_visible)
-                   has_children = TRUE;
-                 node->children = child_node;
-               }
-             g_slist_free (child_paths);
-           }
-
-         node->children = (FileModelNode *)g_slist_reverse ((GSList *)node->children);
-
-         g_signal_connect (node->folder, "deleted",
-                           G_CALLBACK (deleted_callback), node);
-         g_signal_connect (node->folder, "files-added",
-                           G_CALLBACK (files_added_callback), node);
-         g_signal_connect (node->folder, "files-changed",
-                           G_CALLBACK (files_changed_callback), node);
-         g_signal_connect (node->folder, "files-removed",
-                           G_CALLBACK (files_removed_callback), node);
-
-         g_object_set_data (G_OBJECT (node->folder), I_("model-node"), node);
+        {
+         struct GetChildrenData *data;
+         GtkFileSystemHandle *handle;
+
+         data = g_new (struct GetChildrenData, 1);
+         data->model = g_object_ref (model);
+         data->node = node;
+
+         handle =
+           gtk_file_system_get_folder (model->file_system,
+                                       node->path,
+                                       model->types,
+                                       get_children_get_folder_cb,
+                                       data);
+
+         model->pending_handles = g_slist_append (model->pending_handles, handle);
+         node->load_pending = TRUE;
        }
-      
+
       if (is_folder && !has_children)
        {
          /* The hard case ... we claimed this folder had children, but actually
@@ -1460,8 +1710,6 @@ file_model_node_get_children (GtkFileSystemModel *model,
          node->children = child_node;
          node->has_dummy = TRUE;
        }
-
-      node->loaded = TRUE;
     }
 
   return node->children;
index 2057a142e600350367aaf0e4be9176d00d2d4c48..c2fa493db4a97b6d502e24ee25e604208c219282 100644 (file)
@@ -71,7 +71,7 @@ typedef void (*GtkFileSystemModelPathFunc) (GtkFileSystemModel *model,
                                            GtkTreeIter        *iter,
                                            gpointer            user_data);
 
-gboolean _gtk_file_system_model_path_do (GtkFileSystemModel        *model,
+void     _gtk_file_system_model_path_do (GtkFileSystemModel        *model,
                                         const GtkFilePath         *path,
                                         GtkFileSystemModelPathFunc func,
                                         gpointer                   user_data);
index 0dfdcd07839e7a81ac136d6b0cf82e048aa4b4b6..39f98a1aba28c5fb99f9a49b710093d48d8193d6 100644 (file)
@@ -111,10 +111,12 @@ struct _GtkFileFolderUnix
   GtkFileInfoType types;
   gchar *filename;
   GHashTable *stat_info;
+  guint load_folder_id;
   guint have_stat : 1;
   guint have_mime_type : 1;
   guint is_network_dir : 1;
   guint have_hidden : 1;
+  guint is_finished_loading : 1;
   time_t asof;
 };
 
@@ -127,7 +129,8 @@ struct stat_info_entry {
 
 static const GtkFileInfoType STAT_NEEDED_MASK = (GTK_FILE_INFO_IS_FOLDER |
                                                 GTK_FILE_INFO_MODIFICATION_TIME |
-                                                GTK_FILE_INFO_SIZE);
+                                                GTK_FILE_INFO_SIZE |
+                                                GTK_FILE_INFO_ICON);
 
 static GObjectClass *system_parent_class;
 static GObjectClass *folder_parent_class;
@@ -141,13 +144,21 @@ static GSList *             gtk_file_system_unix_list_volumes        (GtkFileSys
 static GtkFileSystemVolume *gtk_file_system_unix_get_volume_for_path (GtkFileSystem     *file_system,
                                                                      const GtkFilePath *path);
 
-static GtkFileFolder *gtk_file_system_unix_get_folder    (GtkFileSystem      *file_system,
-                                                         const GtkFilePath  *path,
-                                                         GtkFileInfoType     types,
-                                                         GError            **error);
-static gboolean       gtk_file_system_unix_create_folder (GtkFileSystem      *file_system,
-                                                         const GtkFilePath  *path,
-                                                         GError            **error);
+static GtkFileSystemHandle *gtk_file_system_unix_get_folder (GtkFileSystem      *file_system,
+                                                            const GtkFilePath  *path,
+                                                            GtkFileInfoType     types,
+                                                            GtkFileSystemGetFolderCallback  callback,
+                                                            gpointer                        data);
+static GtkFileSystemHandle *gtk_file_system_unix_get_info (GtkFileSystem *file_system,
+                                                          const GtkFilePath *path,
+                                                          GtkFileInfoType types,
+                                                          GtkFileSystemGetInfoCallback callback,
+                                                          gpointer data);
+static GtkFileSystemHandle *gtk_file_system_unix_create_folder (GtkFileSystem      *file_system,
+                                                               const GtkFilePath  *path,
+                                                               GtkFileSystemCreateFolderCallback callback,
+                                                               gpointer data);
+static void         gtk_file_system_unix_cancel_operation        (GtkFileSystemHandle *handle);
 
 static void         gtk_file_system_unix_volume_free             (GtkFileSystem       *file_system,
                                                                  GtkFileSystemVolume *volume);
@@ -155,15 +166,14 @@ static GtkFilePath *gtk_file_system_unix_volume_get_base_path    (GtkFileSystem
                                                                  GtkFileSystemVolume *volume);
 static gboolean     gtk_file_system_unix_volume_get_is_mounted   (GtkFileSystem       *file_system,
                                                                  GtkFileSystemVolume *volume);
-static gboolean     gtk_file_system_unix_volume_mount            (GtkFileSystem       *file_system,
+static GtkFileSystemHandle *gtk_file_system_unix_volume_mount    (GtkFileSystem       *file_system,
                                                                  GtkFileSystemVolume *volume,
-                                                                 GError             **error);
+                                                                 GtkFileSystemVolumeMountCallback callback,
+                                                                 gpointer data);
 static gchar *      gtk_file_system_unix_volume_get_display_name (GtkFileSystem       *file_system,
                                                                  GtkFileSystemVolume *volume);
-static GdkPixbuf *  gtk_file_system_unix_volume_render_icon      (GtkFileSystem        *file_system,
+static gchar *      gtk_file_system_unix_volume_get_icon_name    (GtkFileSystem        *file_system,
                                                                  GtkFileSystemVolume  *volume,
-                                                                 GtkWidget            *widget,
-                                                                 gint                  pixel_size,
                                                                  GError              **error);
 
 static gboolean       gtk_file_system_unix_get_parent    (GtkFileSystem      *file_system,
@@ -190,11 +200,6 @@ static GtkFilePath *gtk_file_system_unix_uri_to_path      (GtkFileSystem     *fi
 static GtkFilePath *gtk_file_system_unix_filename_to_path (GtkFileSystem     *file_system,
                                                           const gchar       *filename);
 
-static GdkPixbuf *gtk_file_system_unix_render_icon (GtkFileSystem     *file_system,
-                                                   const GtkFilePath *path,
-                                                   GtkWidget         *widget,
-                                                   gint               pixel_size,
-                                                   GError           **error);
 
 static gboolean gtk_file_system_unix_insert_bookmark (GtkFileSystem     *file_system,
                                                      const GtkFilePath *path,
@@ -229,13 +234,35 @@ static GtkFilePath *filename_to_path   (const gchar       *filename);
 
 static gboolean     filename_is_root  (const char       *filename);
 
+static gboolean get_is_hidden_for_file (const char *filename,
+                                       const char *basename);
 static gboolean file_is_hidden (GtkFileFolderUnix *folder_unix,
                                const char        *basename);
 
-static gboolean fill_in_names (GtkFileFolderUnix *folder_unix, GError **error);
-static void fill_in_stats (GtkFileFolderUnix *folder_unix);
-static void fill_in_mime_type (GtkFileFolderUnix *folder_unix);
-static void fill_in_hidden (GtkFileFolderUnix *folder_unix);
+static GtkFileInfo *file_info_for_root_with_error (const char *root_name,
+                                                  GError **error);
+static gboolean     stat_with_error               (const char *filename,
+                                                  struct stat *statbuf,
+                                                  GError **error);
+static GtkFileInfo *create_file_info              (GtkFileFolderUnix *folder_unix,
+                                                  const char *filename,
+                                                  const char *basename,
+                                                  GtkFileInfoType types,
+                                                  struct stat *statbuf,
+                                                  const char *mime_type);
+
+static gboolean fill_in_names     (GtkFileFolderUnix  *folder_unix,
+                                  GError            **error);
+static void     fill_in_stats     (GtkFileFolderUnix  *folder_unix);
+static void     fill_in_mime_type (GtkFileFolderUnix  *folder_unix);
+static void     fill_in_hidden    (GtkFileFolderUnix  *folder_unix);
+
+static gboolean cb_fill_in_stats     (gpointer key,
+                                     gpointer value,
+                                     gpointer user_data);
+static gboolean cb_fill_in_mime_type (gpointer key,
+                                     gpointer value,
+                                     gpointer user_data);
 
 static char *       get_parent_dir    (const char       *filename);
 
@@ -311,13 +338,15 @@ gtk_file_system_unix_iface_init   (GtkFileSystemIface *iface)
   iface->list_volumes = gtk_file_system_unix_list_volumes;
   iface->get_volume_for_path = gtk_file_system_unix_get_volume_for_path;
   iface->get_folder = gtk_file_system_unix_get_folder;
+  iface->get_info = gtk_file_system_unix_get_info;
   iface->create_folder = gtk_file_system_unix_create_folder;
+  iface->cancel_operation = gtk_file_system_unix_cancel_operation;
   iface->volume_free = gtk_file_system_unix_volume_free;
   iface->volume_get_base_path = gtk_file_system_unix_volume_get_base_path;
   iface->volume_get_is_mounted = gtk_file_system_unix_volume_get_is_mounted;
   iface->volume_mount = gtk_file_system_unix_volume_mount;
   iface->volume_get_display_name = gtk_file_system_unix_volume_get_display_name;
-  iface->volume_render_icon = gtk_file_system_unix_volume_render_icon;
+  iface->volume_get_icon_name = gtk_file_system_unix_volume_get_icon_name;
   iface->get_parent = gtk_file_system_unix_get_parent;
   iface->make_path = gtk_file_system_unix_make_path;
   iface->parse = gtk_file_system_unix_parse;
@@ -325,7 +354,6 @@ gtk_file_system_unix_iface_init   (GtkFileSystemIface *iface)
   iface->path_to_filename = gtk_file_system_unix_path_to_filename;
   iface->uri_to_path = gtk_file_system_unix_uri_to_path;
   iface->filename_to_path = gtk_file_system_unix_filename_to_path;
-  iface->render_icon = gtk_file_system_unix_render_icon;
   iface->insert_bookmark = gtk_file_system_unix_insert_bookmark;
   iface->remove_bookmark = gtk_file_system_unix_remove_bookmark;
   iface->list_bookmarks = gtk_file_system_unix_list_bookmarks;
@@ -395,14 +423,386 @@ remove_trailing_slash (const char *filename)
     return g_memdup (filename, len + 1);
 }
 
-static GtkFileFolder *
-gtk_file_system_unix_get_folder (GtkFileSystem     *file_system,
-                                const GtkFilePath *path,
-                                GtkFileInfoType    types,
-                                GError           **error)
+/* Delay callback dispatching
+ */
+
+enum callback_types
+{
+  CALLBACK_GET_INFO,
+  CALLBACK_GET_FOLDER,
+  CALLBACK_CREATE_FOLDER,
+  CALLBACK_VOLUME_MOUNT
+};
+
+static void queue_callback (enum callback_types type, gpointer data);
+
+struct get_info_callback
+{
+  GtkFileSystemGetInfoCallback callback;
+  GtkFileSystemHandle *handle;
+  GtkFileInfo *file_info;
+  GError *error;
+  gpointer data;
+};
+
+static inline void
+dispatch_get_info_callback (struct get_info_callback *info)
+{
+  (* info->callback) (info->handle, info->file_info, info->error, info->data);
+
+  if (info->file_info)
+    gtk_file_info_free (info->file_info);
+
+  if (info->error)
+    g_error_free (info->error);
+
+  g_object_unref (info->handle);
+
+  g_free (info);
+}
+
+static inline void
+queue_get_info_callback (GtkFileSystemGetInfoCallback  callback,
+                        GtkFileSystemHandle          *handle,
+                        GtkFileInfo                  *file_info,
+                        GError                       *error,
+                        gpointer                      data)
+{
+  struct get_info_callback *info;
+
+  info = g_new (struct get_info_callback, 1);
+  info->callback = callback;
+  info->handle = handle;
+  info->file_info = file_info;
+  info->error = error;
+  info->data = data;
+
+  queue_callback (CALLBACK_GET_INFO, info);
+}
+
+
+struct get_folder_callback
+{
+  GtkFileSystemGetFolderCallback callback;
+  GtkFileSystemHandle *handle;
+  GtkFileFolder *folder;
+  GError *error;
+  gpointer data;
+};
+
+static inline void
+dispatch_get_folder_callback (struct get_folder_callback *info)
+{
+  (* info->callback) (info->handle, info->folder, info->error, info->data);
+
+  if (info->error)
+    g_error_free (info->error);
+
+  g_object_unref (info->handle);
+
+  g_free (info);
+}
+
+static inline void
+queue_get_folder_callback (GtkFileSystemGetFolderCallback  callback,
+                          GtkFileSystemHandle            *handle,
+                          GtkFileFolder                  *folder,
+                          GError                         *error,
+                          gpointer                        data)
+{
+  struct get_folder_callback *info;
+
+  info = g_new (struct get_folder_callback, 1);
+  info->callback = callback;
+  info->handle = handle;
+  info->folder = folder;
+  info->error = error;
+  info->data = data;
+
+  queue_callback (CALLBACK_GET_FOLDER, info);
+}
+
+
+struct create_folder_callback
 {
+  GtkFileSystemCreateFolderCallback callback;
+  GtkFileSystemHandle *handle;
+  GtkFilePath *path;
+  GError *error;
+  gpointer data;
+};
+
+static inline void
+dispatch_create_folder_callback (struct create_folder_callback *info)
+{
+  (* info->callback) (info->handle, info->path, info->error, info->data);
+
+  if (info->error)
+    g_error_free (info->error);
+
+  if (info->path)
+    gtk_file_path_free (info->path);
+
+  g_object_unref (info->handle);
+
+  g_free (info);
+}
+
+static inline void
+queue_create_folder_callback (GtkFileSystemCreateFolderCallback  callback,
+                             GtkFileSystemHandle               *handle,
+                             const GtkFilePath                 *path,
+                             GError                            *error,
+                             gpointer                           data)
+{
+  struct create_folder_callback *info;
+
+  info = g_new (struct create_folder_callback, 1);
+  info->callback = callback;
+  info->handle = handle;
+  info->path = gtk_file_path_copy (path);
+  info->error = error;
+  info->data = data;
+
+  queue_callback (CALLBACK_CREATE_FOLDER, info);
+}
+
+
+struct volume_mount_callback
+{
+  GtkFileSystemVolumeMountCallback callback;
+  GtkFileSystemHandle *handle;
+  GtkFileSystemVolume *volume;
+  GError *error;
+  gpointer data;
+};
+
+static inline void
+dispatch_volume_mount_callback (struct volume_mount_callback *info)
+{
+  (* info->callback) (info->handle, info->volume, info->error, info->data);
+
+  if (info->error)
+    g_error_free (info->error);
+
+  g_object_unref (info->handle);
+
+  g_free (info);
+}
+
+static inline void
+queue_volume_mount_callback (GtkFileSystemVolumeMountCallback  callback,
+                            GtkFileSystemHandle              *handle,
+                            GtkFileSystemVolume              *volume,
+                            GError                           *error,
+                            gpointer                          data)
+{
+  struct volume_mount_callback *info;
+
+  info = g_new (struct volume_mount_callback, 1);
+  info->callback = callback;
+  info->handle = handle;
+  info->volume = volume;
+  info->error = error;
+  info->data = data;
+
+  queue_callback (CALLBACK_VOLUME_MOUNT, info);
+}
+
+
+struct callback_info
+{
+  enum callback_types type;
+
+  union
+  {
+    struct get_info_callback *get_info;
+    struct get_folder_callback *get_folder;
+    struct create_folder_callback *create_folder;
+    struct volume_mount_callback *volume_mount;
+  } info;
+};
+
+
+static guint execute_callbacks_idle_id = 0;
+static GSList *callbacks = NULL;
+
+static gboolean
+execute_callbacks_idle (gpointer data)
+{
+  GSList *l;
+
+  GDK_THREADS_ENTER ();
+
+  for (l = callbacks; l; l = l->next)
+    {
+      struct callback_info *info = l->data;
+
+      switch (info->type)
+        {
+         case CALLBACK_GET_INFO:
+           dispatch_get_info_callback (info->info.get_info);
+           break;
+
+         case CALLBACK_GET_FOLDER:
+           dispatch_get_folder_callback (info->info.get_folder);
+           break;
+
+         case CALLBACK_CREATE_FOLDER:
+           dispatch_create_folder_callback (info->info.create_folder);
+           break;
+
+         case CALLBACK_VOLUME_MOUNT:
+           dispatch_volume_mount_callback (info->info.volume_mount);
+           break;
+        }
+
+      g_free (info);
+    }
+
+  g_slist_free (callbacks);
+  callbacks = NULL;
+
+  execute_callbacks_idle_id = 0;
+
+  GDK_THREADS_LEAVE ();
+
+  return FALSE;
+}
+
+static void
+queue_callback (enum callback_types type, gpointer data)
+{
+  struct callback_info *info;
+
+  info = g_new (struct callback_info, 1);
+  info->type = type;
+
+  switch (type)
+    {
+      case CALLBACK_GET_INFO:
+       info->info.get_info = data;
+       break;
+
+      case CALLBACK_GET_FOLDER:
+       info->info.get_folder = data;
+       break;
+
+      case CALLBACK_CREATE_FOLDER:
+       info->info.create_folder = data;
+       break;
+
+      case CALLBACK_VOLUME_MOUNT:
+       info->info.volume_mount = data;
+       break;
+    }
+
+  callbacks = g_slist_append (callbacks, info);
+
+  if (!execute_callbacks_idle_id)
+    execute_callbacks_idle_id = g_idle_add (execute_callbacks_idle, NULL);
+}
+
+static GtkFileSystemHandle *
+create_handle (GtkFileSystem *file_system)
+{
+  GtkFileSystemHandle *handle;
+
+  handle = g_object_new (GTK_TYPE_FILE_SYSTEM_HANDLE, NULL);
+  handle->file_system = file_system;
+
+  return handle;
+}
+
+
+
+static GtkFileSystemHandle *
+gtk_file_system_unix_get_info (GtkFileSystem                *file_system,
+                              const GtkFilePath            *path,
+                              GtkFileInfoType               types,
+                              GtkFileSystemGetInfoCallback  callback,
+                              gpointer                      data)
+{
+  GError *error = NULL;
+  GtkFileSystemUnix *system_unix;
+  GtkFileSystemHandle *handle;
+  const char *filename;
+  GtkFileInfo *info;
+  gchar *basename;
+  struct stat statbuf;
+  const char *mime_type;
+
+  system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
+  handle = create_handle (file_system);
+
+  filename = gtk_file_path_get_string (path);
+  g_return_val_if_fail (filename != NULL, FALSE);
+  g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
+
+  if (!stat_with_error (filename, &statbuf, &error))
+    {
+      g_object_ref (handle);
+      queue_get_info_callback (callback, handle, NULL, error, data);
+      return handle;
+    }
+
+  if ((types & GTK_FILE_INFO_MIME_TYPE) != 0)
+    mime_type = xdg_mime_get_mime_type_for_file (filename, &statbuf);
+  else
+    mime_type = NULL;
+
+  basename = g_path_get_basename (filename);
+
+  info = create_file_info (NULL, filename, basename, types, &statbuf,
+                           mime_type);
+  g_free (basename);
+  g_object_ref (handle);
+  queue_get_info_callback (callback, handle, info, NULL, data);
+
+  return handle;
+}
+
+static gboolean
+load_folder (gpointer data)
+{
+  GtkFileFolderUnix *folder_unix = data;
+  GSList *children;
+
+  GDK_THREADS_ENTER ();
+
+  if ((folder_unix->types & STAT_NEEDED_MASK) != 0)
+    fill_in_stats (folder_unix);
+
+  if ((folder_unix->types & GTK_FILE_INFO_MIME_TYPE) != 0)
+    fill_in_mime_type (folder_unix);
+
+  if (gtk_file_folder_unix_list_children (GTK_FILE_FOLDER (folder_unix), &children, NULL))
+    {
+      folder_unix->is_finished_loading = TRUE;
+      g_signal_emit_by_name (folder_unix, "files-added", children);
+      gtk_file_paths_free (children);
+    }
+
+  folder_unix->load_folder_id = 0;
+
+  g_signal_emit_by_name (folder_unix, "finished-loading", 0);
+
+  GDK_THREADS_LEAVE ();
+
+  return FALSE;
+}
+
+static GtkFileSystemHandle *
+gtk_file_system_unix_get_folder (GtkFileSystem                  *file_system,
+                                const GtkFilePath              *path,
+                                GtkFileInfoType                 types,
+                                GtkFileSystemGetFolderCallback  callback,
+                                gpointer                        data)
+{
+  GError *error = NULL;
   GtkFileSystemUnix *system_unix;
   GtkFileFolderUnix *folder_unix;
+  GtkFileSystemHandle *handle;
   const char *filename;
   char *filename_copy;
   gboolean set_asof = FALSE;
@@ -413,6 +813,8 @@ gtk_file_system_unix_get_folder (GtkFileSystem     *file_system,
   g_return_val_if_fail (filename != NULL, NULL);
   g_return_val_if_fail (g_path_is_absolute (filename), NULL);
 
+  handle = create_handle (file_system);
+
   filename_copy = remove_trailing_slash (filename);
   folder_unix = g_hash_table_lookup (system_unix->folder_hash, filename_copy);
 
@@ -470,16 +872,19 @@ gtk_file_system_unix_get_folder (GtkFileSystem     *file_system,
       if (result != 0)
        {
          gchar *display_name = g_filename_display_name (filename);
-         g_set_error (error,
+         g_set_error (&error,
                       GTK_FILE_SYSTEM_ERROR,
                       code,
                       _("Error getting information for '%s': %s"),
                       display_name,
                       g_strerror (my_errno));
 
+         g_object_ref (handle);
+         queue_get_folder_callback (callback, handle, NULL, error, data);
+
          g_free (display_name);
          g_free (filename_copy);
-         return NULL;
+         return handle;
        }
 
       folder_unix = g_object_new (GTK_TYPE_FILE_FOLDER_UNIX, NULL);
@@ -487,9 +892,11 @@ gtk_file_system_unix_get_folder (GtkFileSystem     *file_system,
       folder_unix->filename = filename_copy;
       folder_unix->types = types;
       folder_unix->stat_info = NULL;
+      folder_unix->load_folder_id = 0;
       folder_unix->have_mime_type = FALSE;
       folder_unix->have_stat = FALSE;
       folder_unix->have_hidden = FALSE;
+      folder_unix->is_finished_loading = FALSE;
       set_asof = TRUE;
          
       if ((system_unix->have_afs &&
@@ -507,24 +914,28 @@ gtk_file_system_unix_get_folder (GtkFileSystem     *file_system,
                           folder_unix);
     }
 
-  if ((types & STAT_NEEDED_MASK) != 0)
-    fill_in_stats (folder_unix);
-
-  if ((types & GTK_FILE_INFO_MIME_TYPE) != 0)
-    fill_in_mime_type (folder_unix);
-
   if (set_asof)
     folder_unix->asof = time (NULL);
 
-  return GTK_FILE_FOLDER (folder_unix);
+  g_object_ref (handle);
+  queue_get_folder_callback (callback, handle, GTK_FILE_FOLDER (folder_unix), NULL, data);
+
+  /* Start loading the folder contents in an idle */
+  folder_unix->load_folder_id =
+    g_idle_add ((GSourceFunc) load_folder, folder_unix);
+
+  return handle;
 }
 
-static gboolean
-gtk_file_system_unix_create_folder (GtkFileSystem     *file_system,
-                                   const GtkFilePath *path,
-                                   GError           **error)
+static GtkFileSystemHandle *
+gtk_file_system_unix_create_folder (GtkFileSystem                     *file_system,
+                                   const GtkFilePath                 *path,
+                                   GtkFileSystemCreateFolderCallback  callback,
+                                   gpointer                           data)
 {
+  GError *error = NULL;
   GtkFileSystemUnix *system_unix;
+  GtkFileSystemHandle *handle;
   const char *filename;
   gboolean result;
   char *parent, *tmp;
@@ -536,6 +947,8 @@ gtk_file_system_unix_create_folder (GtkFileSystem     *file_system,
   g_return_val_if_fail (filename != NULL, FALSE);
   g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
 
+  handle = create_handle (file_system);
+
   tmp = remove_trailing_slash (filename);
   errno = 0;
   result = mkdir (tmp, 0777) == 0;
@@ -545,18 +958,22 @@ gtk_file_system_unix_create_folder (GtkFileSystem     *file_system,
   if (!result)
     {
       gchar *display_name = g_filename_display_name (filename);
-      g_set_error (error,
+      g_set_error (&error,
                   GTK_FILE_SYSTEM_ERROR,
                   GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
                   _("Error creating directory '%s': %s"),
                   display_name,
                   g_strerror (save_errno));
+      
+      g_object_ref (handle);
+      queue_create_folder_callback (callback, handle, path, error, data);
+
       g_free (display_name);
-      return FALSE;
+      return handle;
     }
 
-  if (filename_is_root (filename))
-    return TRUE; /* hmmm, but with no notification */
+  g_object_ref (handle);
+  queue_create_folder_callback (callback, handle, path, NULL, data);
 
   parent = get_parent_dir (filename);
   if (parent)
@@ -566,34 +983,52 @@ gtk_file_system_unix_create_folder (GtkFileSystem     *file_system,
       folder_unix = g_hash_table_lookup (system_unix->folder_hash, parent);
       if (folder_unix)
        {
-         GtkFileInfoType types;
-         GtkFilePath *parent_path;
          GSList *paths;
-         GtkFileFolder *folder;
+         char *basename;
+         struct stat_info_entry *entry;
 
-         /* This is sort of a hack.  We re-get the folder, to ensure that the
-          * newly-created directory gets read into the folder's info hash table.
-          */
-
-         types = folder_unix->types;
+         /* Make sure the new folder exists in the parent's folder */
+         entry = g_new0 (struct stat_info_entry, 1);
+         if (folder_unix->is_network_dir)
+           {
+             entry->statbuf.st_mode = S_IFDIR;
+             entry->mime_type = g_strdup ("x-directory/normal");
+           }
 
-         parent_path = gtk_file_path_new_dup (parent);
-         folder = gtk_file_system_get_folder (file_system, parent_path, types, NULL);
-         gtk_file_path_free (parent_path);
+         basename = g_path_get_basename (filename);
+         g_hash_table_insert (folder_unix->stat_info,
+                              basename,
+                              entry);
 
-         if (folder)
+         if (folder_unix->have_stat)
            {
-             paths = g_slist_append (NULL, (GtkFilePath *) path);
-             g_signal_emit_by_name (folder, "files-added", paths);
-             g_slist_free (paths);
-             g_object_unref (folder);
+             /* Cheating */
+             if ((folder_unix->types & STAT_NEEDED_MASK) != 0)
+               cb_fill_in_stats (basename, entry, folder_unix);
+
+             if ((folder_unix->types & GTK_FILE_INFO_MIME_TYPE) != 0)
+               cb_fill_in_mime_type (basename, entry, folder_unix);
            }
+
+         paths = g_slist_append (NULL, (GtkFilePath *) path);
+         g_signal_emit_by_name (folder_unix, "files-added", paths);
+         g_slist_free (paths);
        }
 
       g_free (parent);
     }
 
-  return TRUE;
+  return handle;
+}
+
+static void
+gtk_file_system_unix_cancel_operation (GtkFileSystemHandle *handle)
+{
+  /* We don't set "cancelled" to TRUE here, since the actual operation
+   * is executed in the function itself and not in a callback.  So
+   * the operations can never be cancelled (since they will be already
+   * completed at this point.
+   */
 }
 
 static void
@@ -620,16 +1055,24 @@ gtk_file_system_unix_volume_get_is_mounted (GtkFileSystem        *file_system,
   return TRUE;
 }
 
-static gboolean
-gtk_file_system_unix_volume_mount (GtkFileSystem        *file_system,
-                                  GtkFileSystemVolume  *volume,
-                                  GError              **error)
+static GtkFileSystemHandle *
+gtk_file_system_unix_volume_mount (GtkFileSystem                    *file_system,
+                                  GtkFileSystemVolume              *volume,
+                                  GtkFileSystemVolumeMountCallback  callback,
+                                  gpointer                          data)
 {
-  g_set_error (error,
+  GError *error = NULL;
+  GtkFileSystemHandle *handle = create_handle (file_system);
+
+  g_set_error (&error,
               GTK_FILE_SYSTEM_ERROR,
               GTK_FILE_SYSTEM_ERROR_FAILED,
               _("This file system does not support mounting"));
-  return FALSE;
+
+  g_object_ref (handle);
+  queue_volume_mount_callback (callback, handle, volume, error, data);
+
+  return handle;
 }
 
 static gchar *
@@ -691,83 +1134,11 @@ get_icon_type (const char *filename,
   return get_icon_type_from_stat (&statbuf);
 }
 
-typedef struct
-{
-  gint size;
-  GdkPixbuf *pixbuf;
-} IconCacheElement;
-
-static void
-icon_cache_element_free (IconCacheElement *element)
-{
-  if (element->pixbuf)
-    g_object_unref (element->pixbuf);
-  g_slice_free (IconCacheElement, element);
-}
-
-static void
-icon_theme_changed (GtkIconTheme *icon_theme)
-{
-  GHashTable *cache;
-
-  /* Difference from the initial creation is that we don't
-   * reconnect the signal
-   */
-  cache = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                (GDestroyNotify)g_free,
-                                (GDestroyNotify)icon_cache_element_free);
-  g_object_set_data_full (G_OBJECT (icon_theme), I_("gtk-file-icon-cache"),
-                         cache, (GDestroyNotify)g_hash_table_destroy);
-}
-
-static GdkPixbuf *
-get_cached_icon (GtkWidget   *widget,
-                const gchar *name,
-                gint         pixel_size)
-{
-  GtkIconTheme *icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
-  GHashTable *cache = g_object_get_data (G_OBJECT (icon_theme), "gtk-file-icon-cache");
-  IconCacheElement *element;
-
-  if (!cache)
-    {
-      cache = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                    (GDestroyNotify)g_free,
-                                    (GDestroyNotify)icon_cache_element_free);
-
-      g_object_set_data_full (G_OBJECT (icon_theme), I_("gtk-file-icon-cache"),
-                             cache, (GDestroyNotify)g_hash_table_destroy);
-      g_signal_connect (icon_theme, "changed",
-                       G_CALLBACK (icon_theme_changed), NULL);
-    }
-
-  element = g_hash_table_lookup (cache, name);
-  if (!element)
-    {
-      element = g_slice_new0 (IconCacheElement);
-      g_hash_table_insert (cache, g_strdup (name), element);
-    }
-
-  if (element->size != pixel_size)
-    {
-      if (element->pixbuf)
-       g_object_unref (element->pixbuf);
-      element->size = pixel_size;
-      element->pixbuf = gtk_icon_theme_load_icon (icon_theme, name,
-                                                 pixel_size, 0, NULL);
-    }
-
-  return element->pixbuf ? g_object_ref (element->pixbuf) : NULL;
-}
-
 /* Renders a fallback icon from the stock system */
-static GdkPixbuf *
-get_fallback_icon (GtkWidget *widget,
-                  IconType   icon_type,
-                  GError   **error)
+static const gchar *
+get_fallback_icon_name (IconType icon_type)
 {
   const char *stock_name;
-  GdkPixbuf *pixbuf;
 
   switch (icon_type)
     {
@@ -788,38 +1159,18 @@ get_fallback_icon (GtkWidget *widget,
       break;
     }
 
-  pixbuf = gtk_widget_render_icon (widget, stock_name, GTK_ICON_SIZE_SMALL_TOOLBAR, NULL);
-  if (!pixbuf)
-    g_set_error (error,
-                GTK_FILE_SYSTEM_ERROR,
-                GTK_FILE_SYSTEM_ERROR_FAILED,
-                _("Could not get a stock icon for %s"),
-                stock_name);
-
-  return pixbuf;
+  return stock_name;
 }
 
-static GdkPixbuf *
-gtk_file_system_unix_volume_render_icon (GtkFileSystem        *file_system,
-                                        GtkFileSystemVolume  *volume,
-                                        GtkWidget            *widget,
-                                        gint                  pixel_size,
-                                        GError              **error)
+static gchar *
+gtk_file_system_unix_volume_get_icon_name (GtkFileSystem        *file_system,
+                                          GtkFileSystemVolume  *volume,
+                                          GError              **error)
 {
-  GdkPixbuf *pixbuf;
-
-  pixbuf = get_cached_icon (widget, "drive-harddisk", pixel_size);
-  if (pixbuf)
-    return pixbuf;
-
-  pixbuf = get_cached_icon (widget, "gnome-dev-harddisk", pixel_size);
-  if (pixbuf)
-    return pixbuf;
-
-  pixbuf = get_fallback_icon (widget, ICON_BLOCK_DEVICE, error);
-  g_assert (pixbuf != NULL);
-
-  return pixbuf;
+  /* FIXME: maybe we just always want to return GTK_STOCK_HARDDISK here?
+   * or the new tango icon name?
+   */
+  return g_strdup ("gnome-dev-harddisk");
 }
 
 static char *
@@ -1176,26 +1527,21 @@ get_icon_name_for_directory (const char *path)
     return "gnome-fs-desktop";
   else
     return "gnome-fs-directory";
+
+  return NULL;
 }
 
 /* Computes our internal icon type based on a path name; also returns the MIME
  * type in case we come up with ICON_REGULAR.
  */
 static IconType
-get_icon_type_from_path (GtkFileSystemUnix *system_unix,
-                        const GtkFilePath *path,
+get_icon_type_from_path (GtkFileFolderUnix *folder_unix,
+                        struct stat       *statbuf,
+                        const char        *filename,
                         const char       **mime_type)
 {
-  const char *filename;
-  char *dirname;
-  GtkFileFolderUnix *folder_unix;
   IconType icon_type;
 
-  filename = gtk_file_path_get_string (path);
-  dirname = g_path_get_dirname (filename);
-  folder_unix = g_hash_table_lookup (system_unix->folder_hash, dirname);
-  g_free (dirname);
-
   *mime_type = NULL;
 
   if (folder_unix && folder_unix->have_stat)
@@ -1226,6 +1572,9 @@ get_icon_type_from_path (GtkFileSystemUnix *system_unix,
        }
     }
 
+  if (statbuf)
+    return get_icon_type_from_stat (statbuf);
+
   icon_type = get_icon_type (filename, NULL);
   if (icon_type == ICON_REGULAR)
     *mime_type = xdg_mime_get_mime_type_for_file (filename, NULL);
@@ -1234,11 +1583,9 @@ get_icon_type_from_path (GtkFileSystemUnix *system_unix,
 }
 
 /* Renders an icon for a non-ICON_REGULAR file */
-static GdkPixbuf *
-get_special_icon (IconType           icon_type,
-                 const GtkFilePath *path,
-                 GtkWidget         *widget,
-                 gint               pixel_size)
+static const gchar *
+get_special_icon_name (IconType           icon_type,
+                      const gchar       *filename)
 {
   const char *name;
 
@@ -1255,13 +1602,9 @@ get_special_icon (IconType           icon_type,
     case ICON_CHARACTER_DEVICE:
       name = "gnome-fs-chardev";
       break;
-    case ICON_DIRECTORY: {
-      const char *filename;
-
-      filename = gtk_file_path_get_string (path);
-      name = get_icon_name_for_directory (filename);
-      break;
-      }
+    case ICON_DIRECTORY:
+      /* get_icon_name_for_directory() returns a dupped string */
+      return get_icon_name_for_directory (filename);
     case ICON_EXECUTABLE:
       name ="gnome-fs-executable";
       break;
@@ -1276,17 +1619,15 @@ get_special_icon (IconType           icon_type,
       return NULL;
     }
 
-  return get_cached_icon (widget, name, pixel_size);
+  return name;
 }
 
-static GdkPixbuf *
-get_icon_for_mime_type (GtkWidget  *widget,
-                       const char *mime_type,
-                       gint        pixel_size)
+static gchar *
+get_icon_name_for_mime_type (const char *mime_type)
 {
+  char *name;
   const char *separator;
   GString *icon_name;
-  GdkPixbuf *pixbuf;
 
   if (!mime_type)
     return NULL;
@@ -1295,6 +1636,10 @@ get_icon_for_mime_type (GtkWidget  *widget,
   if (!separator)
     return NULL; /* maybe we should return a GError with "invalid MIME-type" */
 
+  /* FIXME: we default to the gnome icon naming for now.  Some question
+   * as below, how are we going to handle a second attempt?
+   */
+#if 0
   icon_name = g_string_new ("");
   g_string_append_len (icon_name, mime_type, separator - mime_type);
   g_string_append_c (icon_name, '-');
@@ -1311,13 +1656,19 @@ get_icon_for_mime_type (GtkWidget  *widget,
   g_string_free (icon_name, TRUE);
   if (pixbuf)
     return pixbuf;
+#endif
 
   icon_name = g_string_new ("gnome-mime-");
   g_string_append_len (icon_name, mime_type, separator - mime_type);
   g_string_append_c (icon_name, '-');
   g_string_append (icon_name, separator + 1);
-  pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
-  g_string_free (icon_name, TRUE);
+  name = icon_name->str;
+  g_string_free (icon_name, FALSE);
+
+  return name;
+
+  /* FIXME: how are we going to implement a second attempt? */
+#if 0
   if (pixbuf)
     return pixbuf;
 
@@ -1327,50 +1678,7 @@ get_icon_for_mime_type (GtkWidget  *widget,
   g_string_free (icon_name, TRUE);
 
   return pixbuf;
-}
-
-static GdkPixbuf *
-gtk_file_system_unix_render_icon (GtkFileSystem     *file_system,
-                                 const GtkFilePath *path,
-                                 GtkWidget         *widget,
-                                 gint               pixel_size,
-                                 GError           **error)
-{
-  GtkFileSystemUnix *system_unix;
-  IconType icon_type;
-  const char *mime_type;
-  GdkPixbuf *pixbuf;
-
-  system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
-
-  icon_type = get_icon_type_from_path (system_unix, path, &mime_type);
-
-  switch (icon_type) {
-  case ICON_NONE:
-    goto fallback;
-
-  case ICON_REGULAR:
-    pixbuf = get_icon_for_mime_type (widget, mime_type, pixel_size);
-    break;
-
-  default:
-    pixbuf = get_special_icon (icon_type, path, widget, pixel_size);
-  }
-
-  if (pixbuf)
-    goto out;
-
- fallback:
-
-  pixbuf = get_cached_icon (widget, "gnome-fs-regular", pixel_size);
-  if (pixbuf)
-    goto out;
-
-  pixbuf = get_fallback_icon (widget, icon_type, error);
-
- out:
-
-  return pixbuf;
+#endif
 }
 
 static void
@@ -1808,6 +2116,12 @@ gtk_file_folder_unix_finalize (GObject *object)
 {
   GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (object);
 
+  if (folder_unix->load_folder_id)
+    {
+      g_source_remove (folder_unix->load_folder_id);
+      folder_unix->load_folder_id = 0;
+    }
+
   g_hash_table_remove (folder_unix->system_unix->folder_hash, folder_unix->filename);
 
   if (folder_unix->stat_info)
@@ -1912,8 +2226,16 @@ create_file_info (GtkFileFolderUnix *folder_unix,
 
   if (types & GTK_FILE_INFO_IS_HIDDEN)
     {
-      if (file_is_hidden (folder_unix, basename))
-       gtk_file_info_set_is_hidden (info, TRUE);
+      if (folder_unix)
+        {
+          if (file_is_hidden (folder_unix, basename))
+           gtk_file_info_set_is_hidden (info, TRUE);
+       }
+      else
+        {
+         if (get_is_hidden_for_file (filename, basename))
+           gtk_file_info_set_is_hidden (info, TRUE);
+        }
     }
 
   if (types & GTK_FILE_INFO_IS_FOLDER)
@@ -1928,6 +2250,40 @@ create_file_info (GtkFileFolderUnix *folder_unix,
   if (types & GTK_FILE_INFO_SIZE)
     gtk_file_info_set_size (info, (gint64) statbuf->st_size);
 
+  if (types & GTK_FILE_INFO_ICON)
+    {
+      IconType icon_type;
+      gboolean free_icon_name = FALSE;
+      const char *icon_name;
+      const char *icon_mime_type;
+
+      icon_type = get_icon_type_from_path (folder_unix, statbuf, filename, &icon_mime_type);
+
+      switch (icon_type)
+        {
+          case ICON_NONE:
+           icon_name = get_fallback_icon_name (icon_type);
+           break;
+
+          case ICON_REGULAR:
+           free_icon_name = TRUE;
+           if (icon_mime_type)
+             icon_name = get_icon_name_for_mime_type (icon_mime_type);
+           else
+             icon_name = get_icon_name_for_mime_type (mime_type);
+            break;
+
+          default:
+           icon_name = get_special_icon_name (icon_type, filename);
+           break;
+        }
+
+      gtk_file_info_set_icon_name (info, icon_name);
+
+      if (free_icon_name)
+       g_free ((char *) icon_name);
+    }
+
   return info;
 }
 
@@ -2052,13 +2408,11 @@ gtk_file_folder_unix_list_children (GtkFileFolder  *folder,
   GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
   GSList *l;
 
-  if (!fill_in_names (folder_unix, error))
-    return FALSE;
-
   *children = NULL;
 
   /* Get the list of basenames.  */
-  g_hash_table_foreach (folder_unix->stat_info, cb_list_children, children);
+  if (folder_unix->stat_info)
+    g_hash_table_foreach (folder_unix->stat_info, cb_list_children, children);
 
   /* Turn basenames into GFilePaths.  */
   for (l = *children; l; l = l->next)
@@ -2075,8 +2429,7 @@ gtk_file_folder_unix_list_children (GtkFileFolder  *folder,
 static gboolean
 gtk_file_folder_unix_is_finished_loading (GtkFileFolder *folder)
 {
-  /* Since we don't do asynchronous loads, we are always finished loading */
-  return TRUE;
+  return GTK_FILE_FOLDER_UNIX (folder)->is_finished_loading;
 }
 
 static void
@@ -2207,23 +2560,40 @@ fill_in_mime_type (GtkFileFolderUnix *folder_unix)
   folder_unix->have_mime_type = TRUE;
 }
 
+static gchar **
+read_hidden_file (const char *dirname)
+{
+  gchar **lines = NULL;
+  gchar *contents;
+  gchar *hidden_file;
+
+  hidden_file = g_build_filename (dirname, HIDDEN_FILENAME, NULL);
+
+  if (g_file_get_contents (hidden_file, &contents, NULL, NULL))
+    {
+      lines = g_strsplit (contents, "\n", -1);
+      g_free (contents);
+    }
+
+  g_free (hidden_file);
+
+  return lines;
+}
+
 static void
 fill_in_hidden (GtkFileFolderUnix *folder_unix)
 {
-  gchar *hidden_file;
-  gchar *contents;
+  gchar **lines;
   
   if (folder_unix->have_hidden)
     return;
 
-  hidden_file = g_build_filename (folder_unix->filename, HIDDEN_FILENAME, NULL);
+  lines = read_hidden_file (folder_unix->filename);
 
-  if (g_file_get_contents (hidden_file, &contents, NULL, NULL))
+  if (lines)
     {
-      gchar **lines; 
       int i;
       
-      lines = g_strsplit (contents, "\n", -1);
       for (i = 0; lines[i]; i++)
        {
          if (lines[i][0])
@@ -2237,10 +2607,8 @@ fill_in_hidden (GtkFileFolderUnix *folder_unix)
        }
       
       g_strfreev (lines);
-      g_free (contents);
     }
 
-  g_free (hidden_file);
   folder_unix->have_hidden = TRUE;
 }
 
@@ -2263,6 +2631,37 @@ filename_is_root (const char *filename)
   return (after_root != NULL && *after_root == '\0');
 }
 
+static gboolean
+get_is_hidden_for_file (const char *filename,
+                       const char *basename)
+{
+  gchar *dirname;
+  gchar **lines;
+  gboolean hidden = FALSE;
+
+  dirname = g_path_get_dirname (filename);
+  lines = read_hidden_file (dirname);
+  g_free (dirname);
+
+  if (lines)
+    {
+      int i;
+      
+      for (i = 0; lines[i]; i++)
+       {
+         if (lines[i][0] && strcmp (lines[i], basename) == 0)
+           {
+             hidden = TRUE;
+             break;
+           }
+       }
+      
+      g_strfreev (lines);
+    }
+
+  return hidden;
+}
+
 static gboolean
 file_is_hidden (GtkFileFolderUnix *folder_unix,
                const char        *basename)
index ec62d32d35ccc715d940577d380a90c8fc81c4af..2688c1ee36c589f283e4c13e979e00405c5764f3 100644 (file)
@@ -65,6 +65,7 @@ struct _ButtonData
   GtkFilePath *path;
   GtkWidget *image;
   GtkWidget *label;
+  GtkFileSystemHandle *handle;
   guint ignore_changes : 1;
   guint file_is_hidden : 1;
 };
@@ -140,6 +141,8 @@ gtk_path_bar_init (GtkPathBar *path_bar)
   GTK_WIDGET_SET_FLAGS (path_bar, GTK_NO_WINDOW);
   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (path_bar), FALSE);
 
+  path_bar->set_path_handle = NULL;
+
   path_bar->spacing = 3;
   path_bar->up_slider_button = get_slider_button (path_bar, GTK_ARROW_LEFT);
   path_bar->down_slider_button = get_slider_button (path_bar, GTK_ARROW_RIGHT);
@@ -247,8 +250,13 @@ remove_settings_signal (GtkPathBar *path_bar,
 static void
 gtk_path_bar_dispose (GObject *object)
 {
-  remove_settings_signal (GTK_PATH_BAR (object),
-                         gtk_widget_get_screen (GTK_WIDGET (object)));
+  GtkPathBar *path_bar = GTK_PATH_BAR (object);
+
+  remove_settings_signal (path_bar, gtk_widget_get_screen (GTK_WIDGET (object)));
+
+  if (path_bar->set_path_handle)
+    gtk_file_system_cancel_operation (path_bar->set_path_handle);
+  path_bar->set_path_handle = NULL;
 
   G_OBJECT_CLASS (gtk_path_bar_parent_class)->dispose (object);
 }
@@ -957,7 +965,11 @@ button_clicked_cb (GtkWidget *button,
   button_list = g_list_find (path_bar->button_list, button_data);
   g_assert (button_list != NULL);
 
+  g_signal_handlers_block_by_func (button,
+                                  G_CALLBACK (button_clicked_cb), data);
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+  g_signal_handlers_unblock_by_func (button,
+                                    G_CALLBACK (button_clicked_cb), data);
 
   if (button_list->prev)
     {
@@ -977,22 +989,79 @@ button_clicked_cb (GtkWidget *button,
                 button_data->path, child_path, child_is_hidden);
 }
 
-static GdkPixbuf *
-get_button_image (GtkPathBar *path_bar,
-                 ButtonType  button_type)
+struct SetButtonImageData
+{
+  GtkPathBar *path_bar;
+  ButtonData *button_data;
+};
+
+static void
+set_button_image_get_info_cb (GtkFileSystemHandle *handle,
+                             const GtkFileInfo   *info,
+                             const GError        *error,
+                             gpointer             user_data)
+{
+  gboolean cancelled = handle->cancelled;
+  GdkPixbuf *pixbuf;
+  struct SetButtonImageData *data = user_data;
+
+  if (handle != data->button_data->handle)
+    goto out;
+
+  data->button_data->handle = NULL;
+
+  if (cancelled || error)
+    goto out;
+
+  pixbuf = gtk_file_info_render_icon (info, GTK_WIDGET (data->path_bar),
+                                     data->path_bar->icon_size, NULL);
+  gtk_image_set_from_pixbuf (GTK_IMAGE (data->button_data->image), pixbuf);
+
+  switch (data->button_data->type)
+    {
+      case HOME_BUTTON:
+       if (data->path_bar->home_icon)
+         g_object_unref (pixbuf);
+       else
+         data->path_bar->home_icon = pixbuf;
+       break;
+
+      case DESKTOP_BUTTON:
+       if (data->path_bar->desktop_icon)
+         g_object_unref (pixbuf);
+       else
+         data->path_bar->desktop_icon = pixbuf;
+       break;
+
+      default:
+       break;
+    };
+
+out:
+  g_free (data);
+  g_object_unref (handle);
+}
+
+static void
+set_button_image (GtkPathBar *path_bar,
+                 ButtonData *button_data)
 {
   GtkFileSystemVolume *volume;
+  struct SetButtonImageData *data;
 
-  switch (button_type)
+  switch (button_data->type)
     {
     case ROOT_BUTTON:
 
       if (path_bar->root_icon != NULL)
-       return path_bar->root_icon;
+        {
+          gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->root_icon);
+         break;
+       }
       
       volume = gtk_file_system_get_volume_for_path (path_bar->file_system, path_bar->root_path);
       if (volume == NULL)
-       return NULL;
+       return;
 
       path_bar->root_icon = gtk_file_system_volume_render_icon (path_bar->file_system,
                                                                volume,
@@ -1001,37 +1070,63 @@ get_button_image (GtkPathBar *path_bar,
                                                                NULL);
       gtk_file_system_volume_free (path_bar->file_system, volume);
 
-      return path_bar->root_icon;
+      gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->root_icon);
+      break;
+
     case HOME_BUTTON:
       if (path_bar->home_icon != NULL)
-       return path_bar->home_icon;
-
-      path_bar->home_icon = gtk_file_system_render_icon (path_bar->file_system,
-                                                        path_bar->home_path,
-                                                        GTK_WIDGET (path_bar),
-                                                        path_bar->icon_size,
-                                                        NULL);
-      return path_bar->home_icon;
+        {
+         gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->home_icon);
+         break;
+       }
+
+      data = g_new0 (struct SetButtonImageData, 1);
+      data->path_bar = path_bar;
+      data->button_data = button_data;
+
+      if (button_data->handle)
+       gtk_file_system_cancel_operation (button_data->handle);
+
+      button_data->handle =
+        gtk_file_system_get_info (path_bar->file_system,
+                                 path_bar->home_path,
+                                 GTK_FILE_INFO_ICON,
+                                 set_button_image_get_info_cb,
+                                 data);
+      break;
+
     case DESKTOP_BUTTON:
       if (path_bar->desktop_icon != NULL)
-       return path_bar->desktop_icon;
-
-      path_bar->desktop_icon = gtk_file_system_render_icon (path_bar->file_system,
-                                                           path_bar->desktop_path,
-                                                           GTK_WIDGET (path_bar),
-                                                           path_bar->icon_size,
-                                                           NULL);
-      return path_bar->desktop_icon;
+        {
+         gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->desktop_icon);
+         break;
+       }
+
+      data = g_new0 (struct SetButtonImageData, 1);
+      data->path_bar = path_bar;
+      data->button_data = button_data;
+
+      if (button_data->handle)
+       gtk_file_system_cancel_operation (button_data->handle);
+
+      button_data->handle =
+        gtk_file_system_get_info (path_bar->file_system,
+                                 path_bar->desktop_path,
+                                 GTK_FILE_INFO_ICON,
+                                 set_button_image_get_info_cb,
+                                 data);
+      break;
     default:
-      return NULL;
+      break;
     }
-  
-  return NULL;
 }
 
 static void
 button_data_free (ButtonData *button_data)
 {
+  if (button_data->handle)
+    gtk_file_system_cancel_operation (button_data->handle);
+
   gtk_file_path_free (button_data->path);
   g_free (button_data->dir_name);
   g_free (button_data);
@@ -1094,9 +1189,7 @@ gtk_path_bar_update_button_appearance (GtkPathBar *path_bar,
 
   if (button_data->image != NULL)
     {
-      GdkPixbuf *pixbuf;
-      pixbuf = get_button_image (path_bar, button_data->type);
-      gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), pixbuf);
+      set_button_image (path_bar, button_data);
     }
 
   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_data->button)) != current_dir)
@@ -1294,126 +1387,174 @@ gtk_path_bar_check_parent_path (GtkPathBar         *path_bar,
   return FALSE;
 }
 
-gboolean
-_gtk_path_bar_set_path (GtkPathBar         *path_bar,
-                       const GtkFilePath  *file_path,
-                       const gboolean      keep_trail,     
-                       GError            **error)
+
+struct SetPathInfo
 {
   GtkFilePath *path;
-  gboolean first_directory = TRUE;
-  gboolean result;
-  GList *new_buttons = NULL;
-  GList *fake_root = NULL;
-
-  g_return_val_if_fail (GTK_IS_PATH_BAR (path_bar), FALSE);
-  g_return_val_if_fail (file_path != NULL, FALSE);
-
-  result = TRUE;
-
-  /* Check whether the new path is already present in the pathbar as buttons.
-   * This could be a parent directory or a previous selected subdirectory.
-   */
-  if (keep_trail &&
-      gtk_path_bar_check_parent_path (path_bar, file_path, path_bar->file_system))
-    return TRUE;
+  GtkFilePath *parent_path;
+  GtkPathBar *path_bar;
+  GList *new_buttons;
+  GList *fake_root;
+  gboolean first_directory;
+};
 
-  path = gtk_file_path_copy (file_path);
+static void
+gtk_path_bar_set_path_finish (struct SetPathInfo *info,
+                              gboolean result)
+{
+  if (result)
+    {
+      GList *l;
 
-  gtk_widget_push_composite_child ();
+      gtk_path_bar_clear_buttons (info->path_bar);
+      info->path_bar->button_list = g_list_reverse (info->new_buttons);
+      info->path_bar->fake_root = info->fake_root;
 
-  while (path != NULL)
-    {
-      GtkFilePath *parent_path = NULL;
-      ButtonData *button_data;
-      const gchar *display_name;
-      gboolean is_hidden;
-      GtkFileFolder *file_folder;
-      GtkFileInfo *file_info;
-      gboolean valid;
-
-      valid = gtk_file_system_get_parent (path_bar->file_system,
-                                         path,
-                                         &parent_path,
-                                         error);
-      if (!valid)
+      for (l = info->path_bar->button_list; l; l = l->next)
        {
-         result = FALSE;
-         gtk_file_path_free (path);
-         break;
+         GtkWidget *button = BUTTON_DATA (l->data)->button;
+         gtk_container_add (GTK_CONTAINER (info->path_bar), button);
        }
+    }
+  else
+    {
+      GList *l;
 
-      file_folder = gtk_file_system_get_folder (path_bar->file_system,
-                                               parent_path ? parent_path : path,
-                                               GTK_FILE_INFO_DISPLAY_NAME | GTK_FILE_INFO_IS_HIDDEN,
-                                               NULL);
-      if (!file_folder)
+      for (l = info->new_buttons; l; l = l->next)
        {
-         result = FALSE;
-         gtk_file_path_free (parent_path);
-         gtk_file_path_free (path);
-         break;
-       }
-
-      file_info = gtk_file_folder_get_info (file_folder, parent_path ? path : NULL, error);
-      g_object_unref (file_folder);
+         ButtonData *button_data;
 
-      if (!file_info)
-       {
-         result = FALSE;
-         gtk_file_path_free (parent_path);
-         gtk_file_path_free (path);
-         break;
+         button_data = BUTTON_DATA (l->data);
+         gtk_widget_destroy (button_data->button);
        }
 
-      display_name = gtk_file_info_get_display_name (file_info);
-      is_hidden = gtk_file_info_get_is_hidden (file_info);
-
-      button_data = make_directory_button (path_bar, display_name, path, first_directory, is_hidden);
-      gtk_file_info_free (file_info);
-      gtk_file_path_free (path);
+      g_list_free (info->new_buttons);
+    }
 
-      new_buttons = g_list_prepend (new_buttons, button_data);
+  if (info->path)
+    gtk_file_path_free (info->path);
+  if (info->parent_path)
+    gtk_file_path_free (info->parent_path);
+  g_free (info);
+}
 
-      if (BUTTON_IS_FAKE_ROOT (button_data))
-       fake_root = new_buttons;
+static void
+gtk_path_bar_get_info_callback (GtkFileSystemHandle *handle,
+                               const GtkFileInfo   *file_info,
+                               const GError        *error,
+                               gpointer             data)
+{
+  gboolean cancelled = handle->cancelled;
+  struct SetPathInfo *path_info = data;
+  ButtonData *button_data;
+  const gchar *display_name;
+  gboolean is_hidden;
+  gboolean valid;
 
-      path = parent_path;
-      first_directory = FALSE;
+  if (handle != path_info->path_bar->set_path_handle)
+    {
+      gtk_path_bar_set_path_finish (path_info, FALSE);
+      g_object_unref (handle);
+      return;
     }
 
-  if (result)
+  g_object_unref (handle);
+  path_info->path_bar->set_path_handle = NULL;
+
+  if (cancelled || !file_info)
     {
-      GList *l;
+      gtk_path_bar_set_path_finish (path_info, FALSE);
+      return;
+    }
 
-      gtk_path_bar_clear_buttons (path_bar);
-      path_bar->button_list = g_list_reverse (new_buttons);
-      path_bar->fake_root = fake_root;
+  display_name = gtk_file_info_get_display_name (file_info);
+  is_hidden = gtk_file_info_get_is_hidden (file_info);
 
-      for (l = path_bar->button_list; l; l = l->next)
-       {
-         GtkWidget *button = BUTTON_DATA (l->data)->button;
-         gtk_container_add (GTK_CONTAINER (path_bar), button);
-       }
+  gtk_widget_push_composite_child ();
+  button_data = make_directory_button (path_info->path_bar, display_name,
+                                       path_info->path,
+                                      path_info->first_directory, is_hidden);
+  gtk_widget_pop_composite_child ();
+  gtk_file_path_free (path_info->path);
+
+  path_info->new_buttons = g_list_prepend (path_info->new_buttons, button_data);
+
+  if (BUTTON_IS_FAKE_ROOT (button_data))
+    path_info->fake_root = path_info->new_buttons;
+
+  path_info->path = path_info->parent_path;
+  path_info->first_directory = FALSE;
+
+  if (!path_info->path)
+    {
+      gtk_path_bar_set_path_finish (path_info, TRUE);
+      return;
     }
-  else
+
+  valid = gtk_file_system_get_parent (path_info->path_bar->file_system,
+                                     path_info->path,
+                                     &path_info->parent_path,
+                                     NULL);
+  if (!valid)
     {
-      GList *l;
+      gtk_path_bar_set_path_finish (path_info, FALSE);
+      return;
+    }
 
-      for (l = new_buttons; l; l = l->next)
-       {
-         ButtonData *button_data;
+  path_info->path_bar->set_path_handle =
+    gtk_file_system_get_info (handle->file_system,
+                             path_info->path,
+                             GTK_FILE_INFO_DISPLAY_NAME | GTK_FILE_INFO_IS_HIDDEN,
+                             gtk_path_bar_get_info_callback,
+                             path_info);
+}
 
-         button_data = BUTTON_DATA (l->data);
-         gtk_widget_destroy (button_data->button);
-       }
+gboolean
+_gtk_path_bar_set_path (GtkPathBar         *path_bar,
+                       const GtkFilePath  *file_path,
+                       const gboolean      keep_trail,     
+                       GError            **error)
+{
+  struct SetPathInfo *info;
+  gboolean result;
+
+  g_return_val_if_fail (GTK_IS_PATH_BAR (path_bar), FALSE);
+  g_return_val_if_fail (file_path != NULL, FALSE);
+
+  result = TRUE;
+
+  /* Check whether the new path is already present in the pathbar as buttons.
+   * This could be a parent directory or a previous selected subdirectory.
+   */
+  if (keep_trail &&
+      gtk_path_bar_check_parent_path (path_bar, file_path, path_bar->file_system))
+    return TRUE;
 
-      g_list_free (new_buttons);
+  info = g_new0 (struct SetPathInfo, 1);
+  info->path = gtk_file_path_copy (file_path);
+  info->path_bar = path_bar;
+  info->first_directory = TRUE;
+
+  result = gtk_file_system_get_parent (path_bar->file_system,
+                                      info->path, &info->parent_path, error);
+  if (!result)
+    {
+      gtk_file_path_free (info->path);
+      g_free (info);
+      return result;
     }
 
-  gtk_widget_pop_composite_child ();
+  if (path_bar->set_path_handle)
+    gtk_file_system_cancel_operation (path_bar->set_path_handle);
+
+  path_bar->set_path_handle =
+    gtk_file_system_get_info (path_bar->file_system,
+                             info->path,
+                             GTK_FILE_INFO_DISPLAY_NAME | GTK_FILE_INFO_IS_HIDDEN,
+                             gtk_path_bar_get_info_callback,
+                             info);
 
-  return result;
+  return TRUE;
 }
 
 /* FIXME: This should be a construct-only property */
index e395ccc0b021a6f8d8a48f5b434b28a71784fca9..9474427b59c99f443ee6262bb592992fbc6daf13 100644 (file)
@@ -45,6 +45,8 @@ struct _GtkPathBar
   GtkFilePath *home_path;
   GtkFilePath *desktop_path;
 
+  GtkFileSystemHandle *set_path_handle;
+
   GdkPixbuf *root_icon;
   GdkPixbuf *home_icon;
   GdkPixbuf *desktop_icon;